68ff352717763a37d3427e7e68f73ef3b535a638
[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  * Bootstrap Body class
853  *
854  * @constructor
855  * Create a new body
856  * @param {Object} config The config object
857  */
858
859 Roo.bootstrap.Body = function(config){
860
861     config = config || {};
862
863     Roo.bootstrap.Body.superclass.constructor.call(this, config);
864     this.el = Roo.get(config.el ? config.el : document.body );
865     if (this.cls && this.cls.length) {
866         Roo.get(document.body).addClass(this.cls);
867     }
868 };
869
870 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
871
872     is_body : true,// just to make sure it's constructed?
873
874         autoCreate : {
875         cls: 'container'
876     },
877     onRender : function(ct, position)
878     {
879        /* Roo.log("Roo.bootstrap.Body - onRender");
880         if (this.cls && this.cls.length) {
881             Roo.get(document.body).addClass(this.cls);
882         }
883         // style??? xttr???
884         */
885     }
886
887
888
889
890 });
891 /*
892  * - LGPL
893  *
894  * button group
895  * 
896  */
897
898
899 /**
900  * @class Roo.bootstrap.ButtonGroup
901  * @extends Roo.bootstrap.Component
902  * Bootstrap ButtonGroup class
903  * @cfg {String} size lg | sm | xs (default empty normal)
904  * @cfg {String} align vertical | justified  (default none)
905  * @cfg {String} direction up | down (default down)
906  * @cfg {Boolean} toolbar false | true
907  * @cfg {Boolean} btn true | false
908  * 
909  * 
910  * @constructor
911  * Create a new Input
912  * @param {Object} config The config object
913  */
914
915 Roo.bootstrap.ButtonGroup = function(config){
916     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 };
918
919 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
920     
921     size: '',
922     align: '',
923     direction: '',
924     toolbar: false,
925     btn: true,
926
927     getAutoCreate : function(){
928         var cfg = {
929             cls: 'btn-group',
930             html : null
931         };
932         
933         cfg.html = this.html || cfg.html;
934         
935         if (this.toolbar) {
936             cfg = {
937                 cls: 'btn-toolbar',
938                 html: null
939             };
940             
941             return cfg;
942         }
943         
944         if (['vertical','justified'].indexOf(this.align)!==-1) {
945             cfg.cls = 'btn-group-' + this.align;
946             
947             if (this.align == 'justified') {
948                 console.log(this.items);
949             }
950         }
951         
952         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
953             cfg.cls += ' btn-group-' + this.size;
954         }
955         
956         if (this.direction == 'up') {
957             cfg.cls += ' dropup' ;
958         }
959         
960         return cfg;
961     },
962     /**
963      * Add a button to the group (similar to NavItem API.)
964      */
965     addItem : function(cfg)
966     {
967         var cn = new Roo.bootstrap.Button(cfg);
968         //this.register(cn);
969         cn.parentId = this.id;
970         cn.onRender(this.el, null);
971         return cn;
972     }
973    
974 });
975
976  /*
977  * - LGPL
978  *
979  * button
980  * 
981  */
982
983 /**
984  * @class Roo.bootstrap.Button
985  * @extends Roo.bootstrap.Component
986  * Bootstrap Button class
987  * @cfg {String} html The button content
988  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
989  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
990  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
991  * @cfg {String} size (lg|sm|xs)
992  * @cfg {String} tag (a|input|submit)
993  * @cfg {String} href empty or href
994  * @cfg {Boolean} disabled default false;
995  * @cfg {Boolean} isClose default false;
996  * @cfg {String} glyphicon depricated - use fa
997  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
998  * @cfg {String} badge text for badge
999  * @cfg {String} theme (default|glow)  
1000  * @cfg {Boolean} inverse dark themed version
1001  * @cfg {Boolean} toggle is it a slidy toggle button
1002  * @cfg {Boolean} pressed   default null - if the button ahs active state
1003  * @cfg {String} ontext text for on slidy toggle state
1004  * @cfg {String} offtext text for off slidy toggle state
1005  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1006  * @cfg {Boolean} removeClass remove the standard class..
1007  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1008  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009  * 
1010  * @constructor
1011  * Create a new button
1012  * @param {Object} config The config object
1013  */
1014
1015
1016 Roo.bootstrap.Button = function(config){
1017     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1018     
1019     this.addEvents({
1020         // raw events
1021         /**
1022          * @event click
1023          * When a button is pressed
1024          * @param {Roo.bootstrap.Button} btn
1025          * @param {Roo.EventObject} e
1026          */
1027         "click" : true,
1028         /**
1029          * @event dblclick
1030          * When a button is double clicked
1031          * @param {Roo.bootstrap.Button} btn
1032          * @param {Roo.EventObject} e
1033          */
1034         "dblclick" : true,
1035          /**
1036          * @event toggle
1037          * After the button has been toggles
1038          * @param {Roo.bootstrap.Button} btn
1039          * @param {Roo.EventObject} e
1040          * @param {boolean} pressed (also available as button.pressed)
1041          */
1042         "toggle" : true
1043     });
1044 };
1045
1046 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1047     html: false,
1048     active: false,
1049     weight: '',
1050     badge_weight: '',
1051     outline : false,
1052     size: '',
1053     tag: 'button',
1054     href: '',
1055     disabled: false,
1056     isClose: false,
1057     glyphicon: '',
1058     fa: '',
1059     badge: '',
1060     theme: 'default',
1061     inverse: false,
1062     
1063     toggle: false,
1064     ontext: 'ON',
1065     offtext: 'OFF',
1066     defaulton: true,
1067     preventDefault: true,
1068     removeClass: false,
1069     name: false,
1070     target: false,
1071     group : false,
1072      
1073     pressed : null,
1074      
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : 'button',
1080             cls : 'roo-button',
1081             html: ''
1082         };
1083         
1084         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1085             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1086             this.tag = 'button';
1087         } else {
1088             cfg.tag = this.tag;
1089         }
1090         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1091         
1092         if (this.toggle == true) {
1093             cfg={
1094                 tag: 'div',
1095                 cls: 'slider-frame roo-button',
1096                 cn: [
1097                     {
1098                         tag: 'span',
1099                         'data-on-text':'ON',
1100                         'data-off-text':'OFF',
1101                         cls: 'slider-button',
1102                         html: this.offtext
1103                     }
1104                 ]
1105             };
1106             // why are we validating the weights?
1107             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1108                 cfg.cls +=  ' ' + this.weight;
1109             }
1110             
1111             return cfg;
1112         }
1113         
1114         if (this.isClose) {
1115             cfg.cls += ' close';
1116             
1117             cfg["aria-hidden"] = true;
1118             
1119             cfg.html = "&times;";
1120             
1121             return cfg;
1122         }
1123              
1124         
1125         if (this.theme==='default') {
1126             cfg.cls = 'btn roo-button';
1127             
1128             //if (this.parentType != 'Navbar') {
1129             this.weight = this.weight.length ?  this.weight : 'default';
1130             //}
1131             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1132                 
1133                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1134                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1135                 cfg.cls += ' btn-' + outline + weight;
1136                 if (this.weight == 'default') {
1137                     // BC
1138                     cfg.cls += ' btn-' + this.weight;
1139                 }
1140             }
1141         } else if (this.theme==='glow') {
1142             
1143             cfg.tag = 'a';
1144             cfg.cls = 'btn-glow roo-button';
1145             
1146             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1147                 
1148                 cfg.cls += ' ' + this.weight;
1149             }
1150         }
1151    
1152         
1153         if (this.inverse) {
1154             this.cls += ' inverse';
1155         }
1156         
1157         
1158         if (this.active || this.pressed === true) {
1159             cfg.cls += ' active';
1160         }
1161         
1162         if (this.disabled) {
1163             cfg.disabled = 'disabled';
1164         }
1165         
1166         if (this.items) {
1167             Roo.log('changing to ul' );
1168             cfg.tag = 'ul';
1169             this.glyphicon = 'caret';
1170             if (Roo.bootstrap.version == 4) {
1171                 this.fa = 'caret-down';
1172             }
1173             
1174         }
1175         
1176         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1177          
1178         //gsRoo.log(this.parentType);
1179         if (this.parentType === 'Navbar' && !this.parent().bar) {
1180             Roo.log('changing to li?');
1181             
1182             cfg.tag = 'li';
1183             
1184             cfg.cls = '';
1185             cfg.cn =  [{
1186                 tag : 'a',
1187                 cls : 'roo-button',
1188                 html : this.html,
1189                 href : this.href || '#'
1190             }];
1191             if (this.menu) {
1192                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1193                 cfg.cls += ' dropdown';
1194             }   
1195             
1196             delete cfg.html;
1197             
1198         }
1199         
1200        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1201         
1202         if (this.glyphicon) {
1203             cfg.html = ' ' + cfg.html;
1204             
1205             cfg.cn = [
1206                 {
1207                     tag: 'span',
1208                     cls: 'glyphicon glyphicon-' + this.glyphicon
1209                 }
1210             ];
1211         }
1212         if (this.fa) {
1213             cfg.html = ' ' + cfg.html;
1214             
1215             cfg.cn = [
1216                 {
1217                     tag: 'i',
1218                     cls: 'fa fas fa-' + this.fa
1219                 }
1220             ];
1221         }
1222         
1223         if (this.badge) {
1224             cfg.html += ' ';
1225             
1226             cfg.tag = 'a';
1227             
1228 //            cfg.cls='btn roo-button';
1229             
1230             cfg.href=this.href;
1231             
1232             var value = cfg.html;
1233             
1234             if(this.glyphicon){
1235                 value = {
1236                     tag: 'span',
1237                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1238                     html: this.html
1239                 };
1240             }
1241             if(this.fa){
1242                 value = {
1243                     tag: 'i',
1244                     cls: 'fa fas fa-' + this.fa,
1245                     html: this.html
1246                 };
1247             }
1248             
1249             var bw = this.badge_weight.length ? this.badge_weight :
1250                 (this.weight.length ? this.weight : 'secondary');
1251             bw = bw == 'default' ? 'secondary' : bw;
1252             
1253             cfg.cn = [
1254                 value,
1255                 {
1256                     tag: 'span',
1257                     cls: 'badge badge-' + bw,
1258                     html: this.badge
1259                 }
1260             ];
1261             
1262             cfg.html='';
1263         }
1264         
1265         if (this.menu) {
1266             cfg.cls += ' dropdown';
1267             cfg.html = typeof(cfg.html) != 'undefined' ?
1268                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269         }
1270         
1271         if (cfg.tag !== 'a' && this.href !== '') {
1272             throw "Tag must be a to set href.";
1273         } else if (this.href.length > 0) {
1274             cfg.href = this.href;
1275         }
1276         
1277         if(this.removeClass){
1278             cfg.cls = '';
1279         }
1280         
1281         if(this.target){
1282             cfg.target = this.target;
1283         }
1284         
1285         return cfg;
1286     },
1287     initEvents: function() {
1288        // Roo.log('init events?');
1289 //        Roo.log(this.el.dom);
1290         // add the menu...
1291         
1292         if (typeof (this.menu) != 'undefined') {
1293             this.menu.parentType = this.xtype;
1294             this.menu.triggerEl = this.el;
1295             this.addxtype(Roo.apply({}, this.menu));
1296         }
1297
1298
1299         if (this.el.hasClass('roo-button')) {
1300              this.el.on('click', this.onClick, this);
1301              this.el.on('dblclick', this.onDblClick, this);
1302         } else {
1303              this.el.select('.roo-button').on('click', this.onClick, this);
1304              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1305              
1306         }
1307         // why?
1308         if(this.removeClass){
1309             this.el.on('click', this.onClick, this);
1310         }
1311         
1312         if (this.group === true) {
1313              if (this.pressed === false || this.pressed === true) {
1314                 // nothing
1315             } else {
1316                 this.pressed = false;
1317                 this.setActive(this.pressed);
1318             }
1319             
1320         }
1321         
1322         this.el.enableDisplayMode();
1323         
1324     },
1325     onClick : function(e)
1326     {
1327         if (this.disabled) {
1328             return;
1329         }
1330         
1331         Roo.log('button on click ');
1332         if(this.preventDefault){
1333             e.preventDefault();
1334         }
1335         
1336         if (this.group) {
1337             if (this.pressed) {
1338                 // do nothing -
1339                 return;
1340             }
1341             this.setActive(true);
1342             var pi = this.parent().items;
1343             for (var i = 0;i < pi.length;i++) {
1344                 if (this == pi[i]) {
1345                     continue;
1346                 }
1347                 if (pi[i].el.hasClass('roo-button')) {
1348                     pi[i].setActive(false);
1349                 }
1350             }
1351             this.fireEvent('click', this, e);            
1352             return;
1353         }
1354         
1355         if (this.pressed === true || this.pressed === false) {
1356             this.toggleActive(e);
1357         }
1358         
1359         
1360         this.fireEvent('click', this, e);
1361     },
1362     onDblClick: function(e)
1363     {
1364         if (this.disabled) {
1365             return;
1366         }
1367         if(this.preventDefault){
1368             e.preventDefault();
1369         }
1370         this.fireEvent('dblclick', this, e);
1371     },
1372     /**
1373      * Enables this button
1374      */
1375     enable : function()
1376     {
1377         this.disabled = false;
1378         this.el.removeClass('disabled');
1379         this.el.dom.removeAttribute("disabled");
1380     },
1381     
1382     /**
1383      * Disable this button
1384      */
1385     disable : function()
1386     {
1387         this.disabled = true;
1388         this.el.addClass('disabled');
1389         this.el.attr("disabled", "disabled")
1390     },
1391      /**
1392      * sets the active state on/off, 
1393      * @param {Boolean} state (optional) Force a particular state
1394      */
1395     setActive : function(v) {
1396         
1397         this.el[v ? 'addClass' : 'removeClass']('active');
1398         this.pressed = v;
1399     },
1400      /**
1401      * toggles the current active state 
1402      */
1403     toggleActive : function(e)
1404     {
1405         this.setActive(!this.pressed); // this modifies pressed...
1406         this.fireEvent('toggle', this, e, this.pressed);
1407     },
1408      /**
1409      * get the current active state
1410      * @return {boolean} true if it's active
1411      */
1412     isActive : function()
1413     {
1414         return this.el.hasClass('active');
1415     },
1416     /**
1417      * set the text of the first selected button
1418      */
1419     setText : function(str)
1420     {
1421         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422     },
1423     /**
1424      * get the text of the first selected button
1425      */
1426     getText : function()
1427     {
1428         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429     },
1430     
1431     setWeight : function(str)
1432     {
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1434         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1435         this.weight = str;
1436         var outline = this.outline ? 'outline-' : '';
1437         if (str == 'default') {
1438             this.el.addClass('btn-default btn-outline-secondary');        
1439             return;
1440         }
1441         this.el.addClass('btn-' + outline + str);        
1442     }
1443     
1444     
1445 });
1446 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1447
1448 Roo.bootstrap.Button.weights = [
1449     'default',
1450     'secondary' ,
1451     'primary',
1452     'success',
1453     'info',
1454     'warning',
1455     'danger',
1456     'link',
1457     'light',
1458     'dark'              
1459    
1460 ];/*
1461  * - LGPL
1462  *
1463  * column
1464  * 
1465  */
1466
1467 /**
1468  * @class Roo.bootstrap.Column
1469  * @extends Roo.bootstrap.Component
1470  * Bootstrap Column class
1471  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1472  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1473  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1474  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1475  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1476  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1477  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1478  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479  *
1480  * 
1481  * @cfg {Boolean} hidden (true|false) hide the element
1482  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1483  * @cfg {String} fa (ban|check|...) font awesome icon
1484  * @cfg {Number} fasize (1|2|....) font awsome size
1485
1486  * @cfg {String} icon (info-sign|check|...) glyphicon name
1487
1488  * @cfg {String} html content of column.
1489  * 
1490  * @constructor
1491  * Create a new Column
1492  * @param {Object} config The config object
1493  */
1494
1495 Roo.bootstrap.Column = function(config){
1496     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 };
1498
1499 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1500     
1501     xs: false,
1502     sm: false,
1503     md: false,
1504     lg: false,
1505     xsoff: false,
1506     smoff: false,
1507     mdoff: false,
1508     lgoff: false,
1509     html: '',
1510     offset: 0,
1511     alert: false,
1512     fa: false,
1513     icon : false,
1514     hidden : false,
1515     fasize : 1,
1516     
1517     getAutoCreate : function(){
1518         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1519         
1520         cfg = {
1521             tag: 'div',
1522             cls: 'column'
1523         };
1524         
1525         var settings=this;
1526         var sizes =   ['xs','sm','md','lg'];
1527         sizes.map(function(size ,ix){
1528             //Roo.log( size + ':' + settings[size]);
1529             
1530             if (settings[size+'off'] !== false) {
1531                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532             }
1533             
1534             if (settings[size] === false) {
1535                 return;
1536             }
1537             
1538             if (!settings[size]) { // 0 = hidden
1539                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1540                 // bootsrap4
1541                 for (var i = ix; i > -1; i--) {
1542                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1543                 }
1544                 
1545                 
1546                 return;
1547             }
1548             cfg.cls += ' col-' + size + '-' + settings[size] + (
1549                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1550             );
1551             
1552         });
1553         
1554         if (this.hidden) {
1555             cfg.cls += ' hidden';
1556         }
1557         
1558         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1559             cfg.cls +=' alert alert-' + this.alert;
1560         }
1561         
1562         
1563         if (this.html.length) {
1564             cfg.html = this.html;
1565         }
1566         if (this.fa) {
1567             var fasize = '';
1568             if (this.fasize > 1) {
1569                 fasize = ' fa-' + this.fasize + 'x';
1570             }
1571             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1572             
1573             
1574         }
1575         if (this.icon) {
1576             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1577         }
1578         
1579         return cfg;
1580     }
1581    
1582 });
1583
1584  
1585
1586  /*
1587  * - LGPL
1588  *
1589  * page container.
1590  * 
1591  */
1592
1593
1594 /**
1595  * @class Roo.bootstrap.Container
1596  * @extends Roo.bootstrap.Component
1597  * @builder-top
1598  * Bootstrap Container class
1599  * @cfg {Boolean} jumbotron is it a jumbotron element
1600  * @cfg {String} html content of element
1601  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1602  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1603  * @cfg {String} header content of header (for panel)
1604  * @cfg {String} footer content of footer (for panel)
1605  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1606  * @cfg {String} tag (header|aside|section) type of HTML tag.
1607  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1608  * @cfg {String} fa font awesome icon
1609  * @cfg {String} icon (info-sign|check|...) glyphicon name
1610  * @cfg {Boolean} hidden (true|false) hide the element
1611  * @cfg {Boolean} expandable (true|false) default false
1612  * @cfg {Boolean} expanded (true|false) default true
1613  * @cfg {String} rheader contet on the right of header
1614  * @cfg {Boolean} clickable (true|false) default false
1615
1616  *     
1617  * @constructor
1618  * Create a new Container
1619  * @param {Object} config The config object
1620  */
1621
1622 Roo.bootstrap.Container = function(config){
1623     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1624     
1625     this.addEvents({
1626         // raw events
1627          /**
1628          * @event expand
1629          * After the panel has been expand
1630          * 
1631          * @param {Roo.bootstrap.Container} this
1632          */
1633         "expand" : true,
1634         /**
1635          * @event collapse
1636          * After the panel has been collapsed
1637          * 
1638          * @param {Roo.bootstrap.Container} this
1639          */
1640         "collapse" : true,
1641         /**
1642          * @event click
1643          * When a element is chick
1644          * @param {Roo.bootstrap.Container} this
1645          * @param {Roo.EventObject} e
1646          */
1647         "click" : true
1648     });
1649 };
1650
1651 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1652     
1653     jumbotron : false,
1654     well: '',
1655     panel : '',
1656     header: '',
1657     footer : '',
1658     sticky: '',
1659     tag : false,
1660     alert : false,
1661     fa: false,
1662     icon : false,
1663     expandable : false,
1664     rheader : '',
1665     expanded : true,
1666     clickable: false,
1667   
1668      
1669     getChildContainer : function() {
1670         
1671         if(!this.el){
1672             return false;
1673         }
1674         
1675         if (this.panel.length) {
1676             return this.el.select('.panel-body',true).first();
1677         }
1678         
1679         return this.el;
1680     },
1681     
1682     
1683     getAutoCreate : function(){
1684         
1685         var cfg = {
1686             tag : this.tag || 'div',
1687             html : '',
1688             cls : ''
1689         };
1690         if (this.jumbotron) {
1691             cfg.cls = 'jumbotron';
1692         }
1693         
1694         
1695         
1696         // - this is applied by the parent..
1697         //if (this.cls) {
1698         //    cfg.cls = this.cls + '';
1699         //}
1700         
1701         if (this.sticky.length) {
1702             
1703             var bd = Roo.get(document.body);
1704             if (!bd.hasClass('bootstrap-sticky')) {
1705                 bd.addClass('bootstrap-sticky');
1706                 Roo.select('html',true).setStyle('height', '100%');
1707             }
1708              
1709             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710         }
1711         
1712         
1713         if (this.well.length) {
1714             switch (this.well) {
1715                 case 'lg':
1716                 case 'sm':
1717                     cfg.cls +=' well well-' +this.well;
1718                     break;
1719                 default:
1720                     cfg.cls +=' well';
1721                     break;
1722             }
1723         }
1724         
1725         if (this.hidden) {
1726             cfg.cls += ' hidden';
1727         }
1728         
1729         
1730         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1731             cfg.cls +=' alert alert-' + this.alert;
1732         }
1733         
1734         var body = cfg;
1735         
1736         if (this.panel.length) {
1737             cfg.cls += ' panel panel-' + this.panel;
1738             cfg.cn = [];
1739             if (this.header.length) {
1740                 
1741                 var h = [];
1742                 
1743                 if(this.expandable){
1744                     
1745                     cfg.cls = cfg.cls + ' expandable';
1746                     
1747                     h.push({
1748                         tag: 'i',
1749                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1750                     });
1751                     
1752                 }
1753                 
1754                 h.push(
1755                     {
1756                         tag: 'span',
1757                         cls : 'panel-title',
1758                         html : (this.expandable ? '&nbsp;' : '') + this.header
1759                     },
1760                     {
1761                         tag: 'span',
1762                         cls: 'panel-header-right',
1763                         html: this.rheader
1764                     }
1765                 );
1766                 
1767                 cfg.cn.push({
1768                     cls : 'panel-heading',
1769                     style : this.expandable ? 'cursor: pointer' : '',
1770                     cn : h
1771                 });
1772                 
1773             }
1774             
1775             body = false;
1776             cfg.cn.push({
1777                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1778                 html : this.html
1779             });
1780             
1781             
1782             if (this.footer.length) {
1783                 cfg.cn.push({
1784                     cls : 'panel-footer',
1785                     html : this.footer
1786                     
1787                 });
1788             }
1789             
1790         }
1791         
1792         if (body) {
1793             body.html = this.html || cfg.html;
1794             // prefix with the icons..
1795             if (this.fa) {
1796                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1797             }
1798             if (this.icon) {
1799                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1800             }
1801             
1802             
1803         }
1804         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1805             cfg.cls =  'container';
1806         }
1807         
1808         return cfg;
1809     },
1810     
1811     initEvents: function() 
1812     {
1813         if(this.expandable){
1814             var headerEl = this.headerEl();
1815         
1816             if(headerEl){
1817                 headerEl.on('click', this.onToggleClick, this);
1818             }
1819         }
1820         
1821         if(this.clickable){
1822             this.el.on('click', this.onClick, this);
1823         }
1824         
1825     },
1826     
1827     onToggleClick : function()
1828     {
1829         var headerEl = this.headerEl();
1830         
1831         if(!headerEl){
1832             return;
1833         }
1834         
1835         if(this.expanded){
1836             this.collapse();
1837             return;
1838         }
1839         
1840         this.expand();
1841     },
1842     
1843     expand : function()
1844     {
1845         if(this.fireEvent('expand', this)) {
1846             
1847             this.expanded = true;
1848             
1849             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1850             
1851             this.el.select('.panel-body',true).first().removeClass('hide');
1852             
1853             var toggleEl = this.toggleEl();
1854
1855             if(!toggleEl){
1856                 return;
1857             }
1858
1859             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1860         }
1861         
1862     },
1863     
1864     collapse : function()
1865     {
1866         if(this.fireEvent('collapse', this)) {
1867             
1868             this.expanded = false;
1869             
1870             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1871             this.el.select('.panel-body',true).first().addClass('hide');
1872         
1873             var toggleEl = this.toggleEl();
1874
1875             if(!toggleEl){
1876                 return;
1877             }
1878
1879             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880         }
1881     },
1882     
1883     toggleEl : function()
1884     {
1885         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886             return;
1887         }
1888         
1889         return this.el.select('.panel-heading .fa',true).first();
1890     },
1891     
1892     headerEl : function()
1893     {
1894         if(!this.el || !this.panel.length || !this.header.length){
1895             return;
1896         }
1897         
1898         return this.el.select('.panel-heading',true).first()
1899     },
1900     
1901     bodyEl : function()
1902     {
1903         if(!this.el || !this.panel.length){
1904             return;
1905         }
1906         
1907         return this.el.select('.panel-body',true).first()
1908     },
1909     
1910     titleEl : function()
1911     {
1912         if(!this.el || !this.panel.length || !this.header.length){
1913             return;
1914         }
1915         
1916         return this.el.select('.panel-title',true).first();
1917     },
1918     
1919     setTitle : function(v)
1920     {
1921         var titleEl = this.titleEl();
1922         
1923         if(!titleEl){
1924             return;
1925         }
1926         
1927         titleEl.dom.innerHTML = v;
1928     },
1929     
1930     getTitle : function()
1931     {
1932         
1933         var titleEl = this.titleEl();
1934         
1935         if(!titleEl){
1936             return '';
1937         }
1938         
1939         return titleEl.dom.innerHTML;
1940     },
1941     
1942     setRightTitle : function(v)
1943     {
1944         var t = this.el.select('.panel-header-right',true).first();
1945         
1946         if(!t){
1947             return;
1948         }
1949         
1950         t.dom.innerHTML = v;
1951     },
1952     
1953     onClick : function(e)
1954     {
1955         e.preventDefault();
1956         
1957         this.fireEvent('click', this, e);
1958     }
1959 });
1960
1961  /*
1962  *  - LGPL
1963  *
1964  *  This is BS4's Card element.. - similar to our containers probably..
1965  * 
1966  */
1967 /**
1968  * @class Roo.bootstrap.Card
1969  * @extends Roo.bootstrap.Component
1970  * Bootstrap Card class
1971  *
1972  *
1973  * possible... may not be implemented..
1974  * @cfg {String} header_image  src url of image.
1975  * @cfg {String|Object} header
1976  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1977  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1978  * 
1979  * @cfg {String} title
1980  * @cfg {String} subtitle
1981  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1982  * @cfg {String} footer
1983  
1984  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1985  * 
1986  * @cfg {String} margin (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1991  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1992  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1993  *
1994  * @cfg {String} padding (0|1|2|3|4|5)
1995  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1996  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1997  * @cfg {String} padding_left (0|1|2|3|4|5)
1998  * @cfg {String} padding_right (0|1|2|3|4|5)
1999  * @cfg {String} padding_x (0|1|2|3|4|5)
2000  * @cfg {String} padding_y (0|1|2|3|4|5)
2001  *
2002  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2007  
2008  * @config {Boolean} dragable  if this card can be dragged.
2009  * @config {String} drag_group  group for drag
2010  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2011  * @config {String} drop_group  group for drag
2012  * 
2013  * @config {Boolean} collapsable can the body be collapsed.
2014  * @config {Boolean} collapsed is the body collapsed when rendered...
2015  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2016  * @config {Boolean} rotated is the body rotated when rendered...
2017  * 
2018  * @constructor
2019  * Create a new Container
2020  * @param {Object} config The config object
2021  */
2022
2023 Roo.bootstrap.Card = function(config){
2024     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2025     
2026     this.addEvents({
2027          // raw events
2028         /**
2029          * @event drop
2030          * When a element a card is dropped
2031          * @param {Roo.bootstrap.Card} this
2032          *
2033          * 
2034          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2035          * @param {String} position 'above' or 'below'
2036          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2037         
2038          */
2039         'drop' : true,
2040          /**
2041          * @event rotate
2042          * When a element a card is rotate
2043          * @param {Roo.bootstrap.Card} this
2044          * @param {Roo.Element} n the node being dropped?
2045          * @param {Boolean} rotate status
2046          */
2047         'rotate' : true,
2048         /**
2049          * @event cardover
2050          * When a card element is dragged over ready to drop (return false to block dropable)
2051          * @param {Roo.bootstrap.Card} this
2052          * @param {Object} data from dragdrop 
2053          */
2054          'cardover' : true
2055          
2056     });
2057 };
2058
2059
2060 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2061     
2062     
2063     weight : '',
2064     
2065     margin: '', /// may be better in component?
2066     margin_top: '', 
2067     margin_bottom: '', 
2068     margin_left: '',
2069     margin_right: '',
2070     margin_x: '',
2071     margin_y: '',
2072     
2073     padding : '',
2074     padding_top: '', 
2075     padding_bottom: '', 
2076     padding_left: '',
2077     padding_right: '',
2078     padding_x: '',
2079     padding_y: '',
2080     
2081     display: '', 
2082     display_xs: '', 
2083     display_sm: '', 
2084     display_lg: '',
2085     display_xl: '',
2086  
2087     header_image  : '',
2088     header : '',
2089     header_size : 0,
2090     title : '',
2091     subtitle : '',
2092     html : '',
2093     footer: '',
2094
2095     collapsable : false,
2096     collapsed : false,
2097     rotateable : false,
2098     rotated : false,
2099     
2100     dragable : false,
2101     drag_group : false,
2102     dropable : false,
2103     drop_group : false,
2104     childContainer : false,
2105     dropEl : false, /// the dom placeholde element that indicates drop location.
2106     containerEl: false, // body container
2107     bodyEl: false, // card-body
2108     headerContainerEl : false, //
2109     headerEl : false,
2110     header_imageEl : false,
2111     
2112     
2113     layoutCls : function()
2114     {
2115         var cls = '';
2116         var t = this;
2117         Roo.log(this.margin_bottom.length);
2118         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2119             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2120             
2121             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2123             }
2124             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2125                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2126             }
2127         });
2128         
2129         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2130             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132             }
2133         });
2134         
2135         // more generic support?
2136         if (this.hidden) {
2137             cls += ' d-none';
2138         }
2139         
2140         return cls;
2141     },
2142  
2143        // Roo.log("Call onRender: " + this.xtype);
2144         /*  We are looking at something like this.
2145 <div class="card">
2146     <img src="..." class="card-img-top" alt="...">
2147     <div class="card-body">
2148         <h5 class="card-title">Card title</h5>
2149          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2150
2151         >> this bit is really the body...
2152         <div> << we will ad dthis in hopefully it will not break shit.
2153         
2154         ** card text does not actually have any styling...
2155         
2156             <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>
2157         
2158         </div> <<
2159           <a href="#" class="card-link">Card link</a>
2160           
2161     </div>
2162     <div class="card-footer">
2163         <small class="text-muted">Last updated 3 mins ago</small>
2164     </div>
2165 </div>
2166          */
2167     getAutoCreate : function(){
2168         
2169         var cfg = {
2170             tag : 'div',
2171             cls : 'card',
2172             cn : [ ]
2173         };
2174         
2175         if (this.weight.length && this.weight != 'light') {
2176             cfg.cls += ' text-white';
2177         } else {
2178             cfg.cls += ' text-dark'; // need as it's nested..
2179         }
2180         if (this.weight.length) {
2181             cfg.cls += ' bg-' + this.weight;
2182         }
2183         
2184         cfg.cls += ' ' + this.layoutCls(); 
2185         
2186         var hdr = false;
2187         var hdr_ctr = false;
2188         if (this.header.length) {
2189             hdr = {
2190                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2191                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2192                 cn : []
2193             };
2194             cfg.cn.push(hdr);
2195             hdr_ctr = hdr;
2196         } else {
2197             hdr = {
2198                 tag : 'div',
2199                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2200                 cn : []
2201             };
2202             cfg.cn.push(hdr);
2203             hdr_ctr = hdr;
2204         }
2205         if (this.collapsable) {
2206             hdr_ctr = {
2207             tag : 'a',
2208             cls : 'd-block user-select-none',
2209             cn: [
2210                     {
2211                         tag: 'i',
2212                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2213                     }
2214                    
2215                 ]
2216             };
2217             hdr.cn.push(hdr_ctr);
2218         }
2219         
2220         hdr_ctr.cn.push(        {
2221             tag: 'span',
2222             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2223             html : this.header
2224         });
2225         
2226         
2227         if (this.header_image.length) {
2228             cfg.cn.push({
2229                 tag : 'img',
2230                 cls : 'card-img-top',
2231                 src: this.header_image // escape?
2232             });
2233         } else {
2234             cfg.cn.push({
2235                     tag : 'div',
2236                     cls : 'card-img-top d-none' 
2237                 });
2238         }
2239             
2240         var body = {
2241             tag : 'div',
2242             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2243             cn : []
2244         };
2245         var obody = body;
2246         if (this.collapsable || this.rotateable) {
2247             obody = {
2248                 tag: 'div',
2249                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2250                 cn : [  body ]
2251             };
2252         }
2253         
2254         cfg.cn.push(obody);
2255         
2256         if (this.title.length) {
2257             body.cn.push({
2258                 tag : 'div',
2259                 cls : 'card-title',
2260                 src: this.title // escape?
2261             });
2262         }  
2263         
2264         if (this.subtitle.length) {
2265             body.cn.push({
2266                 tag : 'div',
2267                 cls : 'card-title',
2268                 src: this.subtitle // escape?
2269             });
2270         }
2271         
2272         body.cn.push({
2273             tag : 'div',
2274             cls : 'roo-card-body-ctr'
2275         });
2276         
2277         if (this.html.length) {
2278             body.cn.push({
2279                 tag: 'div',
2280                 html : this.html
2281             });
2282         }
2283         // fixme ? handle objects?
2284         
2285         if (this.footer.length) {
2286            
2287             cfg.cn.push({
2288                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2289                 html : this.footer
2290             });
2291             
2292         } else {
2293             cfg.cn.push({cls : 'card-footer d-none'});
2294         }
2295         
2296         // footer...
2297         
2298         return cfg;
2299     },
2300     
2301     
2302     getCardHeader : function()
2303     {
2304         var  ret = this.el.select('.card-header',true).first();
2305         if (ret.hasClass('d-none')) {
2306             ret.removeClass('d-none');
2307         }
2308         
2309         return ret;
2310     },
2311     getCardFooter : function()
2312     {
2313         var  ret = this.el.select('.card-footer',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardImageTop : function()
2321     {
2322         var  ret = this.header_imageEl;
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326             
2327         return ret;
2328     },
2329     
2330     getChildContainer : function()
2331     {
2332         
2333         if(!this.el){
2334             return false;
2335         }
2336         return this.el.select('.roo-card-body-ctr',true).first();    
2337     },
2338     
2339     initEvents: function() 
2340     {
2341         this.bodyEl = this.el.select('.card-body',true).first(); 
2342         this.containerEl = this.getChildContainer();
2343         if(this.dragable){
2344             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2345                     containerScroll: true,
2346                     ddGroup: this.drag_group || 'default_card_drag_group'
2347             });
2348             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2349         }
2350         if (this.dropable) {
2351             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2352                 containerScroll: true,
2353                 ddGroup: this.drop_group || 'default_card_drag_group'
2354             });
2355             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2356             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2357             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2358             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2359             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2360         }
2361         
2362         if (this.collapsable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2364         }
2365         if (this.rotateable) {
2366             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2367         }
2368         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2369          
2370         this.footerEl = this.el.select('.card-footer',true).first();
2371         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2372         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2373         this.headerEl = this.el.select('.card-header',true).first();
2374         
2375         if (this.rotated) {
2376             this.el.addClass('roo-card-rotated');
2377             this.fireEvent('rotate', this, true);
2378         }
2379         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2380         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2381         
2382     },
2383     getDragData : function(e)
2384     {
2385         var target = this.getEl();
2386         if (target) {
2387             //this.handleSelection(e);
2388             
2389             var dragData = {
2390                 source: this,
2391                 copy: false,
2392                 nodes: this.getEl(),
2393                 records: []
2394             };
2395             
2396             
2397             dragData.ddel = target.dom ;    // the div element
2398             Roo.log(target.getWidth( ));
2399             dragData.ddel.style.width = target.getWidth() + 'px';
2400             
2401             return dragData;
2402         }
2403         return false;
2404     },
2405     /**
2406     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2407     *    whole Element becomes the target, and this causes the drop gesture to append.
2408     *
2409     *    Returns an object:
2410     *     {
2411            
2412            position : 'below' or 'above'
2413            card  : relateive to card OBJECT (or true for no cards listed)
2414            items_n : relative to nth item in list
2415            card_n : relative to  nth card in list
2416     }
2417     *
2418     *    
2419     */
2420     getTargetFromEvent : function(e, dragged_card_el)
2421     {
2422         var target = e.getTarget();
2423         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2424             target = target.parentNode;
2425         }
2426         
2427         var ret = {
2428             position: '',
2429             cards : [],
2430             card_n : -1,
2431             items_n : -1,
2432             card : false 
2433         };
2434         
2435         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2436         // see if target is one of the 'cards'...
2437         
2438         
2439         //Roo.log(this.items.length);
2440         var pos = false;
2441         
2442         var last_card_n = 0;
2443         var cards_len  = 0;
2444         for (var i = 0;i< this.items.length;i++) {
2445             
2446             if (!this.items[i].el.hasClass('card')) {
2447                  continue;
2448             }
2449             pos = this.getDropPoint(e, this.items[i].el.dom);
2450             
2451             cards_len = ret.cards.length;
2452             //Roo.log(this.items[i].el.dom.id);
2453             ret.cards.push(this.items[i]);
2454             last_card_n  = i;
2455             if (ret.card_n < 0 && pos == 'above') {
2456                 ret.position = cards_len > 0 ? 'below' : pos;
2457                 ret.items_n = i > 0 ? i - 1 : 0;
2458                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2459                 ret.card = ret.cards[ret.card_n];
2460             }
2461         }
2462         if (!ret.cards.length) {
2463             ret.card = true;
2464             ret.position = 'below';
2465             ret.items_n;
2466             return ret;
2467         }
2468         // could not find a card.. stick it at the end..
2469         if (ret.card_n < 0) {
2470             ret.card_n = last_card_n;
2471             ret.card = ret.cards[last_card_n];
2472             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2473             ret.position = 'below';
2474         }
2475         
2476         if (this.items[ret.items_n].el == dragged_card_el) {
2477             return false;
2478         }
2479         
2480         if (ret.position == 'below') {
2481             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2482             
2483             if (card_after  && card_after.el == dragged_card_el) {
2484                 return false;
2485             }
2486             return ret;
2487         }
2488         
2489         // its's after ..
2490         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2491         
2492         if (card_before  && card_before.el == dragged_card_el) {
2493             return false;
2494         }
2495         
2496         return ret;
2497     },
2498     
2499     onNodeEnter : function(n, dd, e, data){
2500         return false;
2501     },
2502     onNodeOver : function(n, dd, e, data)
2503     {
2504        
2505         var target_info = this.getTargetFromEvent(e,data.source.el);
2506         if (target_info === false) {
2507             this.dropPlaceHolder('hide');
2508             return false;
2509         }
2510         Roo.log(['getTargetFromEvent', target_info ]);
2511         
2512         
2513         if (this.fireEvent('cardover', this, [ data ]) === false) {
2514             return false;
2515         }
2516         
2517         this.dropPlaceHolder('show', target_info,data);
2518         
2519         return false; 
2520     },
2521     onNodeOut : function(n, dd, e, data){
2522         this.dropPlaceHolder('hide');
2523      
2524     },
2525     onNodeDrop : function(n, dd, e, data)
2526     {
2527         
2528         // call drop - return false if
2529         
2530         // this could actually fail - if the Network drops..
2531         // we will ignore this at present..- client should probably reload
2532         // the whole set of cards if stuff like that fails.
2533         
2534         
2535         var info = this.getTargetFromEvent(e,data.source.el);
2536         if (info === false) {
2537             return false;
2538         }
2539         this.dropPlaceHolder('hide');
2540   
2541           
2542     
2543         this.acceptCard(data.source, info.position, info.card, info.items_n);
2544         return true;
2545          
2546     },
2547     firstChildCard : function()
2548     {
2549         for (var i = 0;i< this.items.length;i++) {
2550             
2551             if (!this.items[i].el.hasClass('card')) {
2552                  continue;
2553             }
2554             return this.items[i];
2555         }
2556         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2557     },
2558     /**
2559      * accept card
2560      *
2561      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2562      */
2563     acceptCard : function(move_card,  position, next_to_card )
2564     {
2565         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566             return false;
2567         }
2568         
2569         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2570         
2571         move_card.parent().removeCard(move_card);
2572         
2573         
2574         var dom = move_card.el.dom;
2575         dom.style.width = ''; // clear with - which is set by drag.
2576         
2577         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2578             var cardel = next_to_card.el.dom;
2579             
2580             if (position == 'above' ) {
2581                 cardel.parentNode.insertBefore(dom, cardel);
2582             } else if (cardel.nextSibling) {
2583                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2584             } else {
2585                 cardel.parentNode.append(dom);
2586             }
2587         } else {
2588             // card container???
2589             this.containerEl.dom.append(dom);
2590         }
2591         
2592         //FIXME HANDLE card = true 
2593         
2594         // add this to the correct place in items.
2595         
2596         // remove Card from items.
2597         
2598        
2599         if (this.items.length) {
2600             var nitems = [];
2601             //Roo.log([info.items_n, info.position, this.items.length]);
2602             for (var i =0; i < this.items.length; i++) {
2603                 if (i == to_items_n && position == 'above') {
2604                     nitems.push(move_card);
2605                 }
2606                 nitems.push(this.items[i]);
2607                 if (i == to_items_n && position == 'below') {
2608                     nitems.push(move_card);
2609                 }
2610             }
2611             this.items = nitems;
2612             Roo.log(this.items);
2613         } else {
2614             this.items.push(move_card);
2615         }
2616         
2617         move_card.parentId = this.id;
2618         
2619         return true;
2620         
2621         
2622     },
2623     removeCard : function(c)
2624     {
2625         this.items = this.items.filter(function(e) { return e != c });
2626  
2627         var dom = c.el.dom;
2628         dom.parentNode.removeChild(dom);
2629         dom.style.width = ''; // clear with - which is set by drag.
2630         c.parentId = false;
2631         
2632     },
2633     
2634     /**    Decide whether to drop above or below a View node. */
2635     getDropPoint : function(e, n, dd)
2636     {
2637         if (dd) {
2638              return false;
2639         }
2640         if (n == this.containerEl.dom) {
2641             return "above";
2642         }
2643         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2644         var c = t + (b - t) / 2;
2645         var y = Roo.lib.Event.getPageY(e);
2646         if(y <= c) {
2647             return "above";
2648         }else{
2649             return "below";
2650         }
2651     },
2652     onToggleCollapse : function(e)
2653         {
2654         if (this.collapsed) {
2655             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2656             this.collapsableEl.addClass('show');
2657             this.collapsed = false;
2658             return;
2659         }
2660         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2661         this.collapsableEl.removeClass('show');
2662         this.collapsed = true;
2663         
2664     
2665     },
2666     
2667     onToggleRotate : function(e)
2668     {
2669         this.collapsableEl.removeClass('show');
2670         this.footerEl.removeClass('d-none');
2671         this.el.removeClass('roo-card-rotated');
2672         this.el.removeClass('d-none');
2673         if (this.rotated) {
2674             
2675             this.collapsableEl.addClass('show');
2676             this.rotated = false;
2677             this.fireEvent('rotate', this, this.rotated);
2678             return;
2679         }
2680         this.el.addClass('roo-card-rotated');
2681         this.footerEl.addClass('d-none');
2682         this.el.select('.roo-collapsable').removeClass('show');
2683         
2684         this.rotated = true;
2685         this.fireEvent('rotate', this, this.rotated);
2686     
2687     },
2688     
2689     dropPlaceHolder: function (action, info, data)
2690     {
2691         if (this.dropEl === false) {
2692             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693             cls : 'd-none'
2694             },true);
2695         }
2696         this.dropEl.removeClass(['d-none', 'd-block']);        
2697         if (action == 'hide') {
2698             
2699             this.dropEl.addClass('d-none');
2700             return;
2701         }
2702         // FIXME - info.card == true!!!
2703         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2704         
2705         if (info.card !== true) {
2706             var cardel = info.card.el.dom;
2707             
2708             if (info.position == 'above') {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2710             } else if (cardel.nextSibling) {
2711                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2712             } else {
2713                 cardel.parentNode.append(this.dropEl.dom);
2714             }
2715         } else {
2716             // card container???
2717             this.containerEl.dom.append(this.dropEl.dom);
2718         }
2719         
2720         this.dropEl.addClass('d-block roo-card-dropzone');
2721         
2722         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2723         
2724         
2725     
2726     
2727     
2728     },
2729     setHeaderText: function(html)
2730     {
2731         this.header = html;
2732         if (this.headerContainerEl) {
2733             this.headerContainerEl.dom.innerHTML = html;
2734         }
2735     },
2736     onHeaderImageLoad : function(ev, he)
2737     {
2738         if (!this.header_image_fit_square) {
2739             return;
2740         }
2741         
2742         var hw = he.naturalHeight / he.naturalWidth;
2743         // wide image = < 0
2744         // tall image = > 1
2745         //var w = he.dom.naturalWidth;
2746         var ww = he.width;
2747         he.style.left =  0;
2748         he.style.position =  'relative';
2749         if (hw > 1) {
2750             var nw = (ww * (1/hw));
2751             Roo.get(he).setSize( ww * (1/hw),  ww);
2752             he.style.left =  ((ww - nw)/ 2) + 'px';
2753             he.style.position =  'relative';
2754         }
2755
2756     }
2757
2758     
2759 });
2760
2761 /*
2762  * - LGPL
2763  *
2764  * Card header - holder for the card header elements.
2765  * 
2766  */
2767
2768 /**
2769  * @class Roo.bootstrap.CardHeader
2770  * @extends Roo.bootstrap.Element
2771  * Bootstrap CardHeader class
2772  * @constructor
2773  * Create a new Card Header - that you can embed children into
2774  * @param {Object} config The config object
2775  */
2776
2777 Roo.bootstrap.CardHeader = function(config){
2778     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2779 };
2780
2781 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2782     
2783     
2784     container_method : 'getCardHeader' 
2785     
2786      
2787     
2788     
2789    
2790 });
2791
2792  
2793
2794  /*
2795  * - LGPL
2796  *
2797  * Card footer - holder for the card footer elements.
2798  * 
2799  */
2800
2801 /**
2802  * @class Roo.bootstrap.CardFooter
2803  * @extends Roo.bootstrap.Element
2804  * Bootstrap CardFooter class
2805  * @constructor
2806  * Create a new Card Footer - that you can embed children into
2807  * @param {Object} config The config object
2808  */
2809
2810 Roo.bootstrap.CardFooter = function(config){
2811     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2812 };
2813
2814 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2815     
2816     
2817     container_method : 'getCardFooter' 
2818     
2819      
2820     
2821     
2822    
2823 });
2824
2825  
2826
2827  /*
2828  * - LGPL
2829  *
2830  * Card header - holder for the card header elements.
2831  * 
2832  */
2833
2834 /**
2835  * @class Roo.bootstrap.CardImageTop
2836  * @extends Roo.bootstrap.Element
2837  * Bootstrap CardImageTop class
2838  * @constructor
2839  * Create a new Card Image Top container
2840  * @param {Object} config The config object
2841  */
2842
2843 Roo.bootstrap.CardImageTop = function(config){
2844     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2845 };
2846
2847 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2848     
2849    
2850     container_method : 'getCardImageTop' 
2851     
2852      
2853     
2854    
2855 });
2856
2857  
2858
2859  
2860 /*
2861 * Licence: LGPL
2862 */
2863
2864 /**
2865  * @class Roo.bootstrap.ButtonUploader
2866  * @extends Roo.bootstrap.Button
2867  * Bootstrap Button Uploader class - it's a button which when you add files to it
2868  *
2869  * 
2870  * @cfg {Number} errorTimeout default 3000
2871  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2872  * @cfg {Array}  html The button text.
2873  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2874  *
2875  * @constructor
2876  * Create a new CardUploader
2877  * @param {Object} config The config object
2878  */
2879
2880 Roo.bootstrap.ButtonUploader = function(config){
2881     
2882  
2883     
2884     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2885     
2886      
2887      this.addEvents({
2888          // raw events
2889         /**
2890          * @event beforeselect
2891          * When button is pressed, before show upload files dialog is shown
2892          * @param {Roo.bootstrap.UploaderButton} this
2893          *
2894          */
2895         'beforeselect' : true,
2896          /**
2897          * @event fired when files have been selected, 
2898          * When a the download link is clicked
2899          * @param {Roo.bootstrap.UploaderButton} this
2900          * @param {Array} Array of files that have been uploaded
2901          */
2902         'uploaded' : true
2903         
2904     });
2905 };
2906  
2907 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2908     
2909      
2910     errorTimeout : 3000,
2911      
2912     images : false,
2913    
2914     fileCollection : false,
2915     allowBlank : true,
2916     
2917     multiple : true,
2918     
2919     getAutoCreate : function()
2920     {
2921         var im = {
2922             tag: 'input',
2923             type : 'file',
2924             cls : 'd-none  roo-card-upload-selector' 
2925           
2926         };
2927         if (this.multiple) {
2928             im.multiple = 'multiple';
2929         }
2930         
2931         return  {
2932             cls :'div' ,
2933             cn : [
2934                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2935                 im
2936
2937             ]
2938         };
2939            
2940          
2941     },
2942      
2943    
2944     initEvents : function()
2945     {
2946         
2947         Roo.bootstrap.Button.prototype.initEvents.call(this);
2948         
2949         
2950         
2951         
2952         
2953         this.urlAPI = (window.createObjectURL && window) || 
2954                                 (window.URL && URL.revokeObjectURL && URL) || 
2955                                 (window.webkitURL && webkitURL);
2956                         
2957          
2958          
2959          
2960         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2961         
2962         this.selectorEl.on('change', this.onFileSelected, this);
2963          
2964          
2965        
2966     },
2967     
2968    
2969     onClick : function(e)
2970     {
2971         e.preventDefault();
2972         
2973         if ( this.fireEvent('beforeselect', this) === false) {
2974             return;
2975         }
2976          
2977         this.selectorEl.dom.click();
2978          
2979     },
2980     
2981     onFileSelected : function(e)
2982     {
2983         e.preventDefault();
2984         
2985         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2986             return;
2987         }
2988         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2989         this.selectorEl.dom.value  = '';// hopefully reset..
2990         
2991         this.fireEvent('uploaded', this,  files );
2992         
2993     },
2994     
2995        
2996    
2997     
2998     /**
2999      * addCard - add an Attachment to the uploader
3000      * @param data - the data about the image to upload
3001      *
3002      * {
3003           id : 123
3004           title : "Title of file",
3005           is_uploaded : false,
3006           src : "http://.....",
3007           srcfile : { the File upload object },
3008           mimetype : file.type,
3009           preview : false,
3010           is_deleted : 0
3011           .. any other data...
3012         }
3013      *
3014      * 
3015     */
3016      
3017     reset: function()
3018     {
3019          
3020          this.selectorEl
3021     } 
3022     
3023     
3024     
3025     
3026 });
3027  /*
3028  * - LGPL
3029  *
3030  * image
3031  * 
3032  */
3033
3034
3035 /**
3036  * @class Roo.bootstrap.Img
3037  * @extends Roo.bootstrap.Component
3038  * Bootstrap Img class
3039  * @cfg {Boolean} imgResponsive false | true
3040  * @cfg {String} border rounded | circle | thumbnail
3041  * @cfg {String} src image source
3042  * @cfg {String} alt image alternative text
3043  * @cfg {String} href a tag href
3044  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3045  * @cfg {String} xsUrl xs image source
3046  * @cfg {String} smUrl sm image source
3047  * @cfg {String} mdUrl md image source
3048  * @cfg {String} lgUrl lg image source
3049  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3050  * 
3051  * @constructor
3052  * Create a new Input
3053  * @param {Object} config The config object
3054  */
3055
3056 Roo.bootstrap.Img = function(config){
3057     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3058     
3059     this.addEvents({
3060         // img events
3061         /**
3062          * @event click
3063          * The img click event for the img.
3064          * @param {Roo.EventObject} e
3065          */
3066         "click" : true,
3067         /**
3068          * @event load
3069          * The when any image loads
3070          * @param {Roo.EventObject} e
3071          */
3072         "load" : true
3073     });
3074 };
3075
3076 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3077     
3078     imgResponsive: true,
3079     border: '',
3080     src: 'about:blank',
3081     href: false,
3082     target: false,
3083     xsUrl: '',
3084     smUrl: '',
3085     mdUrl: '',
3086     lgUrl: '',
3087     backgroundContain : false,
3088
3089     getAutoCreate : function()
3090     {   
3091         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3092             return this.createSingleImg();
3093         }
3094         
3095         var cfg = {
3096             tag: 'div',
3097             cls: 'roo-image-responsive-group',
3098             cn: []
3099         };
3100         var _this = this;
3101         
3102         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3103             
3104             if(!_this[size + 'Url']){
3105                 return;
3106             }
3107             
3108             var img = {
3109                 tag: 'img',
3110                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3111                 html: _this.html || cfg.html,
3112                 src: _this[size + 'Url']
3113             };
3114             
3115             img.cls += ' roo-image-responsive-' + size;
3116             
3117             var s = ['xs', 'sm', 'md', 'lg'];
3118             
3119             s.splice(s.indexOf(size), 1);
3120             
3121             Roo.each(s, function(ss){
3122                 img.cls += ' hidden-' + ss;
3123             });
3124             
3125             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3126                 cfg.cls += ' img-' + _this.border;
3127             }
3128             
3129             if(_this.alt){
3130                 cfg.alt = _this.alt;
3131             }
3132             
3133             if(_this.href){
3134                 var a = {
3135                     tag: 'a',
3136                     href: _this.href,
3137                     cn: [
3138                         img
3139                     ]
3140                 };
3141
3142                 if(this.target){
3143                     a.target = _this.target;
3144                 }
3145             }
3146             
3147             cfg.cn.push((_this.href) ? a : img);
3148             
3149         });
3150         
3151         return cfg;
3152     },
3153     
3154     createSingleImg : function()
3155     {
3156         var cfg = {
3157             tag: 'img',
3158             cls: (this.imgResponsive) ? 'img-responsive' : '',
3159             html : null,
3160             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3161         };
3162         
3163         if (this.backgroundContain) {
3164             cfg.cls += ' background-contain';
3165         }
3166         
3167         cfg.html = this.html || cfg.html;
3168         
3169         if (this.backgroundContain) {
3170             cfg.style="background-image: url(" + this.src + ')';
3171         } else {
3172             cfg.src = this.src || cfg.src;
3173         }
3174         
3175         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3176             cfg.cls += ' img-' + this.border;
3177         }
3178         
3179         if(this.alt){
3180             cfg.alt = this.alt;
3181         }
3182         
3183         if(this.href){
3184             var a = {
3185                 tag: 'a',
3186                 href: this.href,
3187                 cn: [
3188                     cfg
3189                 ]
3190             };
3191             
3192             if(this.target){
3193                 a.target = this.target;
3194             }
3195             
3196         }
3197         
3198         return (this.href) ? a : cfg;
3199     },
3200     
3201     initEvents: function() 
3202     {
3203         if(!this.href){
3204             this.el.on('click', this.onClick, this);
3205         }
3206         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3207             this.el.on('load', this.onImageLoad, this);
3208         } else {
3209             // not sure if this works.. not tested
3210             this.el.select('img', true).on('load', this.onImageLoad, this);
3211         }
3212         
3213     },
3214     
3215     onClick : function(e)
3216     {
3217         Roo.log('img onclick');
3218         this.fireEvent('click', this, e);
3219     },
3220     onImageLoad: function(e)
3221     {
3222         Roo.log('img load');
3223         this.fireEvent('load', this, e);
3224     },
3225     
3226     /**
3227      * Sets the url of the image - used to update it
3228      * @param {String} url the url of the image
3229      */
3230     
3231     setSrc : function(url)
3232     {
3233         this.src =  url;
3234         
3235         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3236             if (this.backgroundContain) {
3237                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3238             } else {
3239                 this.el.dom.src =  url;
3240             }
3241             return;
3242         }
3243         
3244         this.el.select('img', true).first().dom.src =  url;
3245     }
3246     
3247     
3248    
3249 });
3250
3251  /*
3252  * - LGPL
3253  *
3254  * image
3255  * 
3256  */
3257
3258
3259 /**
3260  * @class Roo.bootstrap.Link
3261  * @extends Roo.bootstrap.Component
3262  * Bootstrap Link Class
3263  * @cfg {String} alt image alternative text
3264  * @cfg {String} href a tag href
3265  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3266  * @cfg {String} html the content of the link.
3267  * @cfg {String} anchor name for the anchor link
3268  * @cfg {String} fa - favicon
3269
3270  * @cfg {Boolean} preventDefault (true | false) default false
3271
3272  * 
3273  * @constructor
3274  * Create a new Input
3275  * @param {Object} config The config object
3276  */
3277
3278 Roo.bootstrap.Link = function(config){
3279     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3280     
3281     this.addEvents({
3282         // img events
3283         /**
3284          * @event click
3285          * The img click event for the img.
3286          * @param {Roo.EventObject} e
3287          */
3288         "click" : true
3289     });
3290 };
3291
3292 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3293     
3294     href: false,
3295     target: false,
3296     preventDefault: false,
3297     anchor : false,
3298     alt : false,
3299     fa: false,
3300
3301
3302     getAutoCreate : function()
3303     {
3304         var html = this.html || '';
3305         
3306         if (this.fa !== false) {
3307             html = '<i class="fa fa-' + this.fa + '"></i>';
3308         }
3309         var cfg = {
3310             tag: 'a'
3311         };
3312         // anchor's do not require html/href...
3313         if (this.anchor === false) {
3314             cfg.html = html;
3315             cfg.href = this.href || '#';
3316         } else {
3317             cfg.name = this.anchor;
3318             if (this.html !== false || this.fa !== false) {
3319                 cfg.html = html;
3320             }
3321             if (this.href !== false) {
3322                 cfg.href = this.href;
3323             }
3324         }
3325         
3326         if(this.alt !== false){
3327             cfg.alt = this.alt;
3328         }
3329         
3330         
3331         if(this.target !== false) {
3332             cfg.target = this.target;
3333         }
3334         
3335         return cfg;
3336     },
3337     
3338     initEvents: function() {
3339         
3340         if(!this.href || this.preventDefault){
3341             this.el.on('click', this.onClick, this);
3342         }
3343     },
3344     
3345     onClick : function(e)
3346     {
3347         if(this.preventDefault){
3348             e.preventDefault();
3349         }
3350         //Roo.log('img onclick');
3351         this.fireEvent('click', this, e);
3352     }
3353    
3354 });
3355
3356  /*
3357  * - LGPL
3358  *
3359  * header
3360  * 
3361  */
3362
3363 /**
3364  * @class Roo.bootstrap.Header
3365  * @extends Roo.bootstrap.Component
3366  * Bootstrap Header class
3367  * @cfg {String} html content of header
3368  * @cfg {Number} level (1|2|3|4|5|6) default 1
3369  * 
3370  * @constructor
3371  * Create a new Header
3372  * @param {Object} config The config object
3373  */
3374
3375
3376 Roo.bootstrap.Header  = function(config){
3377     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3378 };
3379
3380 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3381     
3382     //href : false,
3383     html : false,
3384     level : 1,
3385     
3386     
3387     
3388     getAutoCreate : function(){
3389         
3390         
3391         
3392         var cfg = {
3393             tag: 'h' + (1 *this.level),
3394             html: this.html || ''
3395         } ;
3396         
3397         return cfg;
3398     }
3399    
3400 });
3401
3402  
3403
3404  /*
3405  * Based on:
3406  * Ext JS Library 1.1.1
3407  * Copyright(c) 2006-2007, Ext JS, LLC.
3408  *
3409  * Originally Released Under LGPL - original licence link has changed is not relivant.
3410  *
3411  * Fork - LGPL
3412  * <script type="text/javascript">
3413  */
3414  
3415 /**
3416  * @class Roo.bootstrap.MenuMgr
3417  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3418  * @singleton
3419  */
3420 Roo.bootstrap.MenuMgr = function(){
3421    var menus, active, groups = {}, attached = false, lastShow = new Date();
3422
3423    // private - called when first menu is created
3424    function init(){
3425        menus = {};
3426        active = new Roo.util.MixedCollection();
3427        Roo.get(document).addKeyListener(27, function(){
3428            if(active.length > 0){
3429                hideAll();
3430            }
3431        });
3432    }
3433
3434    // private
3435    function hideAll(){
3436        if(active && active.length > 0){
3437            var c = active.clone();
3438            c.each(function(m){
3439                m.hide();
3440            });
3441        }
3442    }
3443
3444    // private
3445    function onHide(m){
3446        active.remove(m);
3447        if(active.length < 1){
3448            Roo.get(document).un("mouseup", onMouseDown);
3449             
3450            attached = false;
3451        }
3452    }
3453
3454    // private
3455    function onShow(m){
3456        var last = active.last();
3457        lastShow = new Date();
3458        active.add(m);
3459        if(!attached){
3460           Roo.get(document).on("mouseup", onMouseDown);
3461            
3462            attached = true;
3463        }
3464        if(m.parentMenu){
3465           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3466           m.parentMenu.activeChild = m;
3467        }else if(last && last.isVisible()){
3468           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3469        }
3470    }
3471
3472    // private
3473    function onBeforeHide(m){
3474        if(m.activeChild){
3475            m.activeChild.hide();
3476        }
3477        if(m.autoHideTimer){
3478            clearTimeout(m.autoHideTimer);
3479            delete m.autoHideTimer;
3480        }
3481    }
3482
3483    // private
3484    function onBeforeShow(m){
3485        var pm = m.parentMenu;
3486        if(!pm && !m.allowOtherMenus){
3487            hideAll();
3488        }else if(pm && pm.activeChild && active != m){
3489            pm.activeChild.hide();
3490        }
3491    }
3492
3493    // private this should really trigger on mouseup..
3494    function onMouseDown(e){
3495         Roo.log("on Mouse Up");
3496         
3497         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3498             Roo.log("MenuManager hideAll");
3499             hideAll();
3500             e.stopEvent();
3501         }
3502         
3503         
3504    }
3505
3506    // private
3507    function onBeforeCheck(mi, state){
3508        if(state){
3509            var g = groups[mi.group];
3510            for(var i = 0, l = g.length; i < l; i++){
3511                if(g[i] != mi){
3512                    g[i].setChecked(false);
3513                }
3514            }
3515        }
3516    }
3517
3518    return {
3519
3520        /**
3521         * Hides all menus that are currently visible
3522         */
3523        hideAll : function(){
3524             hideAll();  
3525        },
3526
3527        // private
3528        register : function(menu){
3529            if(!menus){
3530                init();
3531            }
3532            menus[menu.id] = menu;
3533            menu.on("beforehide", onBeforeHide);
3534            menu.on("hide", onHide);
3535            menu.on("beforeshow", onBeforeShow);
3536            menu.on("show", onShow);
3537            var g = menu.group;
3538            if(g && menu.events["checkchange"]){
3539                if(!groups[g]){
3540                    groups[g] = [];
3541                }
3542                groups[g].push(menu);
3543                menu.on("checkchange", onCheck);
3544            }
3545        },
3546
3547         /**
3548          * Returns a {@link Roo.menu.Menu} object
3549          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3550          * be used to generate and return a new Menu instance.
3551          */
3552        get : function(menu){
3553            if(typeof menu == "string"){ // menu id
3554                return menus[menu];
3555            }else if(menu.events){  // menu instance
3556                return menu;
3557            }
3558            /*else if(typeof menu.length == 'number'){ // array of menu items?
3559                return new Roo.bootstrap.Menu({items:menu});
3560            }else{ // otherwise, must be a config
3561                return new Roo.bootstrap.Menu(menu);
3562            }
3563            */
3564            return false;
3565        },
3566
3567        // private
3568        unregister : function(menu){
3569            delete menus[menu.id];
3570            menu.un("beforehide", onBeforeHide);
3571            menu.un("hide", onHide);
3572            menu.un("beforeshow", onBeforeShow);
3573            menu.un("show", onShow);
3574            var g = menu.group;
3575            if(g && menu.events["checkchange"]){
3576                groups[g].remove(menu);
3577                menu.un("checkchange", onCheck);
3578            }
3579        },
3580
3581        // private
3582        registerCheckable : function(menuItem){
3583            var g = menuItem.group;
3584            if(g){
3585                if(!groups[g]){
3586                    groups[g] = [];
3587                }
3588                groups[g].push(menuItem);
3589                menuItem.on("beforecheckchange", onBeforeCheck);
3590            }
3591        },
3592
3593        // private
3594        unregisterCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                groups[g].remove(menuItem);
3598                menuItem.un("beforecheckchange", onBeforeCheck);
3599            }
3600        }
3601    };
3602 }();/*
3603  * - LGPL
3604  *
3605  * menu
3606  * 
3607  */
3608
3609 /**
3610  * @class Roo.bootstrap.Menu
3611  * @extends Roo.bootstrap.Component
3612  * Bootstrap Menu class - container for MenuItems
3613  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3614  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3615  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3616  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3617   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3618   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3619  
3620  * @constructor
3621  * Create a new Menu
3622  * @param {Object} config The config object
3623  */
3624
3625
3626 Roo.bootstrap.Menu = function(config){
3627     
3628     if (config.type == 'treeview') {
3629         // normally menu's are drawn attached to the document to handle layering etc..
3630         // however treeview (used by the docs menu is drawn into the parent element)
3631         this.container_method = 'getChildContainer'; 
3632     }
3633     
3634     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3635     if (this.registerMenu && this.type != 'treeview')  {
3636         Roo.bootstrap.MenuMgr.register(this);
3637     }
3638     
3639     
3640     this.addEvents({
3641         /**
3642          * @event beforeshow
3643          * Fires before this menu is displayed (return false to block)
3644          * @param {Roo.menu.Menu} this
3645          */
3646         beforeshow : true,
3647         /**
3648          * @event beforehide
3649          * Fires before this menu is hidden (return false to block)
3650          * @param {Roo.menu.Menu} this
3651          */
3652         beforehide : true,
3653         /**
3654          * @event show
3655          * Fires after this menu is displayed
3656          * @param {Roo.menu.Menu} this
3657          */
3658         show : true,
3659         /**
3660          * @event hide
3661          * Fires after this menu is hidden
3662          * @param {Roo.menu.Menu} this
3663          */
3664         hide : true,
3665         /**
3666          * @event click
3667          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3668          * @param {Roo.menu.Menu} this
3669          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3670          * @param {Roo.EventObject} e
3671          */
3672         click : true,
3673         /**
3674          * @event mouseover
3675          * Fires when the mouse is hovering over this menu
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.EventObject} e
3678          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3679          */
3680         mouseover : true,
3681         /**
3682          * @event mouseout
3683          * Fires when the mouse exits this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseout : true,
3689         /**
3690          * @event itemclick
3691          * Fires when a menu item contained in this menu is clicked
3692          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3693          * @param {Roo.EventObject} e
3694          */
3695         itemclick: true
3696     });
3697     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3698 };
3699
3700 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3701     
3702    /// html : false,
3703    
3704     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3705     type: false,
3706     /**
3707      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3708      */
3709     registerMenu : true,
3710     
3711     menuItems :false, // stores the menu items..
3712     
3713     hidden:true,
3714         
3715     parentMenu : false,
3716     
3717     stopEvent : true,
3718     
3719     isLink : false,
3720     
3721     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3722     
3723     hideTrigger : false,
3724     
3725     align : 'tl-bl?',
3726     
3727     
3728     getChildContainer : function() {
3729         return this.el;  
3730     },
3731     
3732     getAutoCreate : function(){
3733          
3734         //if (['right'].indexOf(this.align)!==-1) {
3735         //    cfg.cn[1].cls += ' pull-right'
3736         //}
3737          
3738         var cfg = {
3739             tag : 'ul',
3740             cls : 'dropdown-menu shadow' ,
3741             style : 'z-index:1000'
3742             
3743         };
3744         
3745         if (this.type === 'submenu') {
3746             cfg.cls = 'submenu active';
3747         }
3748         if (this.type === 'treeview') {
3749             cfg.cls = 'treeview-menu';
3750         }
3751         
3752         return cfg;
3753     },
3754     initEvents : function() {
3755         
3756        // Roo.log("ADD event");
3757        // Roo.log(this.triggerEl.dom);
3758         if (this.triggerEl) {
3759             
3760             this.triggerEl.on('click', this.onTriggerClick, this);
3761             
3762             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3763             
3764             if (!this.hideTrigger) {
3765                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3766                     // dropdown toggle on the 'a' in BS4?
3767                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3768                 } else {
3769                     this.triggerEl.addClass('dropdown-toggle');
3770                 }
3771             }
3772         }
3773         
3774         if (Roo.isTouch) {
3775             this.el.on('touchstart'  , this.onTouch, this);
3776         }
3777         this.el.on('click' , this.onClick, this);
3778
3779         this.el.on("mouseover", this.onMouseOver, this);
3780         this.el.on("mouseout", this.onMouseOut, this);
3781         
3782     },
3783     
3784     findTargetItem : function(e)
3785     {
3786         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3787         if(!t){
3788             return false;
3789         }
3790         //Roo.log(t);         Roo.log(t.id);
3791         if(t && t.id){
3792             //Roo.log(this.menuitems);
3793             return this.menuitems.get(t.id);
3794             
3795             //return this.items.get(t.menuItemId);
3796         }
3797         
3798         return false;
3799     },
3800     
3801     onTouch : function(e) 
3802     {
3803         Roo.log("menu.onTouch");
3804         //e.stopEvent(); this make the user popdown broken
3805         this.onClick(e);
3806     },
3807     
3808     onClick : function(e)
3809     {
3810         Roo.log("menu.onClick");
3811         
3812         var t = this.findTargetItem(e);
3813         if(!t || t.isContainer){
3814             return;
3815         }
3816         Roo.log(e);
3817         /*
3818         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3819             if(t == this.activeItem && t.shouldDeactivate(e)){
3820                 this.activeItem.deactivate();
3821                 delete this.activeItem;
3822                 return;
3823             }
3824             if(t.canActivate){
3825                 this.setActiveItem(t, true);
3826             }
3827             return;
3828             
3829             
3830         }
3831         */
3832        
3833         Roo.log('pass click event');
3834         
3835         t.onClick(e);
3836         
3837         this.fireEvent("click", this, t, e);
3838         
3839         var _this = this;
3840         
3841         if(!t.href.length || t.href == '#'){
3842             (function() { _this.hide(); }).defer(100);
3843         }
3844         
3845     },
3846     
3847     onMouseOver : function(e){
3848         var t  = this.findTargetItem(e);
3849         //Roo.log(t);
3850         //if(t){
3851         //    if(t.canActivate && !t.disabled){
3852         //        this.setActiveItem(t, true);
3853         //    }
3854         //}
3855         
3856         this.fireEvent("mouseover", this, e, t);
3857     },
3858     isVisible : function(){
3859         return !this.hidden;
3860     },
3861     onMouseOut : function(e){
3862         var t  = this.findTargetItem(e);
3863         
3864         //if(t ){
3865         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3866         //        this.activeItem.deactivate();
3867         //        delete this.activeItem;
3868         //    }
3869         //}
3870         this.fireEvent("mouseout", this, e, t);
3871     },
3872     
3873     
3874     /**
3875      * Displays this menu relative to another element
3876      * @param {String/HTMLElement/Roo.Element} element The element to align to
3877      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3878      * the element (defaults to this.defaultAlign)
3879      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3880      */
3881     show : function(el, pos, parentMenu)
3882     {
3883         if (false === this.fireEvent("beforeshow", this)) {
3884             Roo.log("show canceled");
3885             return;
3886         }
3887         this.parentMenu = parentMenu;
3888         if(!this.el){
3889             this.render();
3890         }
3891         this.el.addClass('show'); // show otherwise we do not know how big we are..
3892          
3893         var xy = this.el.getAlignToXY(el, pos);
3894         
3895         // bl-tl << left align  below
3896         // tl-bl << left align 
3897         
3898         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3899             // if it goes to far to the right.. -> align left.
3900             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3901         }
3902         if(xy[0] < 0){
3903             // was left align - go right?
3904             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3905         }
3906         
3907         // goes down the bottom
3908         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3909            xy[1]  < 0 ){
3910             var a = this.align.replace('?', '').split('-');
3911             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3912             
3913         }
3914         
3915         this.showAt(  xy , parentMenu, false);
3916     },
3917      /**
3918      * Displays this menu at a specific xy position
3919      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3920      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3921      */
3922     showAt : function(xy, parentMenu, /* private: */_e){
3923         this.parentMenu = parentMenu;
3924         if(!this.el){
3925             this.render();
3926         }
3927         if(_e !== false){
3928             this.fireEvent("beforeshow", this);
3929             //xy = this.el.adjustForConstraints(xy);
3930         }
3931         
3932         //this.el.show();
3933         this.hideMenuItems();
3934         this.hidden = false;
3935         if (this.triggerEl) {
3936             this.triggerEl.addClass('open');
3937         }
3938         
3939         this.el.addClass('show');
3940         
3941         
3942         
3943         // reassign x when hitting right
3944         
3945         // reassign y when hitting bottom
3946         
3947         // but the list may align on trigger left or trigger top... should it be a properity?
3948         
3949         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3950             this.el.setXY(xy);
3951         }
3952         
3953         this.focus();
3954         this.fireEvent("show", this);
3955     },
3956     
3957     focus : function(){
3958         return;
3959         if(!this.hidden){
3960             this.doFocus.defer(50, this);
3961         }
3962     },
3963
3964     doFocus : function(){
3965         if(!this.hidden){
3966             this.focusEl.focus();
3967         }
3968     },
3969
3970     /**
3971      * Hides this menu and optionally all parent menus
3972      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3973      */
3974     hide : function(deep)
3975     {
3976         if (false === this.fireEvent("beforehide", this)) {
3977             Roo.log("hide canceled");
3978             return;
3979         }
3980         this.hideMenuItems();
3981         if(this.el && this.isVisible()){
3982            
3983             if(this.activeItem){
3984                 this.activeItem.deactivate();
3985                 this.activeItem = null;
3986             }
3987             if (this.triggerEl) {
3988                 this.triggerEl.removeClass('open');
3989             }
3990             
3991             this.el.removeClass('show');
3992             this.hidden = true;
3993             this.fireEvent("hide", this);
3994         }
3995         if(deep === true && this.parentMenu){
3996             this.parentMenu.hide(true);
3997         }
3998     },
3999     
4000     onTriggerClick : function(e)
4001     {
4002         Roo.log('trigger click');
4003         
4004         var target = e.getTarget();
4005         
4006         Roo.log(target.nodeName.toLowerCase());
4007         
4008         if(target.nodeName.toLowerCase() === 'i'){
4009             e.preventDefault();
4010         }
4011         
4012     },
4013     
4014     onTriggerPress  : function(e)
4015     {
4016         Roo.log('trigger press');
4017         //Roo.log(e.getTarget());
4018        // Roo.log(this.triggerEl.dom);
4019        
4020         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4021         var pel = Roo.get(e.getTarget());
4022         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4023             Roo.log('is treeview or dropdown?');
4024             return;
4025         }
4026         
4027         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028             return;
4029         }
4030         
4031         if (this.isVisible()) {
4032             Roo.log('hide');
4033             this.hide();
4034         } else {
4035             Roo.log('show');
4036             
4037             this.show(this.triggerEl, this.align, false);
4038         }
4039         
4040         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4041             e.stopEvent();
4042         }
4043         
4044     },
4045        
4046     
4047     hideMenuItems : function()
4048     {
4049         Roo.log("hide Menu Items");
4050         if (!this.el) { 
4051             return;
4052         }
4053         
4054         this.el.select('.open',true).each(function(aa) {
4055             
4056             aa.removeClass('open');
4057          
4058         });
4059     },
4060     addxtypeChild : function (tree, cntr) {
4061         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4062           
4063         this.menuitems.add(comp);
4064         return comp;
4065
4066     },
4067     getEl : function()
4068     {
4069         Roo.log(this.el);
4070         return this.el;
4071     },
4072     
4073     clear : function()
4074     {
4075         this.getEl().dom.innerHTML = '';
4076         this.menuitems.clear();
4077     }
4078 });
4079
4080  
4081  /*
4082  * - LGPL
4083  *
4084  * menu item
4085  * 
4086  */
4087
4088
4089 /**
4090  * @class Roo.bootstrap.MenuItem
4091  * @extends Roo.bootstrap.Component
4092  * Bootstrap MenuItem class
4093  * @cfg {String} html the menu label
4094  * @cfg {String} href the link
4095  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4096  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4097  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4098  * @cfg {String} fa favicon to show on left of menu item.
4099  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100  * 
4101  * 
4102  * @constructor
4103  * Create a new MenuItem
4104  * @param {Object} config The config object
4105  */
4106
4107
4108 Roo.bootstrap.MenuItem = function(config){
4109     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4110     this.addEvents({
4111         // raw events
4112         /**
4113          * @event click
4114          * The raw click event for the entire grid.
4115          * @param {Roo.bootstrap.MenuItem} this
4116          * @param {Roo.EventObject} e
4117          */
4118         "click" : true
4119     });
4120 };
4121
4122 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4123     
4124     href : false,
4125     html : false,
4126     preventDefault: false,
4127     isContainer : false,
4128     active : false,
4129     fa: false,
4130     
4131     getAutoCreate : function(){
4132         
4133         if(this.isContainer){
4134             return {
4135                 tag: 'li',
4136                 cls: 'dropdown-menu-item '
4137             };
4138         }
4139         var ctag = {
4140             tag: 'span',
4141             html: 'Link'
4142         };
4143         
4144         var anc = {
4145             tag : 'a',
4146             cls : 'dropdown-item',
4147             href : '#',
4148             cn : [  ]
4149         };
4150         
4151         if (this.fa !== false) {
4152             anc.cn.push({
4153                 tag : 'i',
4154                 cls : 'fa fa-' + this.fa
4155             });
4156         }
4157         
4158         anc.cn.push(ctag);
4159         
4160         
4161         var cfg= {
4162             tag: 'li',
4163             cls: 'dropdown-menu-item',
4164             cn: [ anc ]
4165         };
4166         if (this.parent().type == 'treeview') {
4167             cfg.cls = 'treeview-menu';
4168         }
4169         if (this.active) {
4170             cfg.cls += ' active';
4171         }
4172         
4173         
4174         
4175         anc.href = this.href || cfg.cn[0].href ;
4176         ctag.html = this.html || cfg.cn[0].html ;
4177         return cfg;
4178     },
4179     
4180     initEvents: function()
4181     {
4182         if (this.parent().type == 'treeview') {
4183             this.el.select('a').on('click', this.onClick, this);
4184         }
4185         
4186         if (this.menu) {
4187             this.menu.parentType = this.xtype;
4188             this.menu.triggerEl = this.el;
4189             this.menu = this.addxtype(Roo.apply({}, this.menu));
4190         }
4191         
4192     },
4193     onClick : function(e)
4194     {
4195         Roo.log('item on click ');
4196         
4197         if(this.preventDefault){
4198             e.preventDefault();
4199         }
4200         //this.parent().hideMenuItems();
4201         
4202         this.fireEvent('click', this, e);
4203     },
4204     getEl : function()
4205     {
4206         return this.el;
4207     } 
4208 });
4209
4210  
4211
4212  /*
4213  * - LGPL
4214  *
4215  * menu separator
4216  * 
4217  */
4218
4219
4220 /**
4221  * @class Roo.bootstrap.MenuSeparator
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap MenuSeparator class
4224  * 
4225  * @constructor
4226  * Create a new MenuItem
4227  * @param {Object} config The config object
4228  */
4229
4230
4231 Roo.bootstrap.MenuSeparator = function(config){
4232     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4233 };
4234
4235 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4236     
4237     getAutoCreate : function(){
4238         var cfg = {
4239             cls: 'divider',
4240             tag : 'li'
4241         };
4242         
4243         return cfg;
4244     }
4245    
4246 });
4247
4248  
4249
4250  
4251 /*
4252 * Licence: LGPL
4253 */
4254
4255 /**
4256  * @class Roo.bootstrap.Modal
4257  * @extends Roo.bootstrap.Component
4258  * @builder-top
4259  * Bootstrap Modal class
4260  * @cfg {String} title Title of dialog
4261  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4262  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4263  * @cfg {Boolean} specificTitle default false
4264  * @cfg {Array} buttons Array of buttons or standard button set..
4265  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4266  * @cfg {Boolean} animate default true
4267  * @cfg {Boolean} allow_close default true
4268  * @cfg {Boolean} fitwindow default false
4269  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4270  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4271  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4272  * @cfg {String} size (sm|lg|xl) default empty
4273  * @cfg {Number} max_width set the max width of modal
4274  * @cfg {Boolean} editableTitle can the title be edited
4275
4276  *
4277  *
4278  * @constructor
4279  * Create a new Modal Dialog
4280  * @param {Object} config The config object
4281  */
4282
4283 Roo.bootstrap.Modal = function(config){
4284     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285     this.addEvents({
4286         // raw events
4287         /**
4288          * @event btnclick
4289          * The raw btnclick event for the button
4290          * @param {Roo.EventObject} e
4291          */
4292         "btnclick" : true,
4293         /**
4294          * @event resize
4295          * Fire when dialog resize
4296          * @param {Roo.bootstrap.Modal} this
4297          * @param {Roo.EventObject} e
4298          */
4299         "resize" : true,
4300         /**
4301          * @event titlechanged
4302          * Fire when the editable title has been changed
4303          * @param {Roo.bootstrap.Modal} this
4304          * @param {Roo.EventObject} value
4305          */
4306         "titlechanged" : true 
4307         
4308     });
4309     this.buttons = this.buttons || [];
4310
4311     if (this.tmpl) {
4312         this.tmpl = Roo.factory(this.tmpl);
4313     }
4314
4315 };
4316
4317 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4318
4319     title : 'test dialog',
4320
4321     buttons : false,
4322
4323     // set on load...
4324
4325     html: false,
4326
4327     tmp: false,
4328
4329     specificTitle: false,
4330
4331     buttonPosition: 'right',
4332
4333     allow_close : true,
4334
4335     animate : true,
4336
4337     fitwindow: false,
4338     
4339      // private
4340     dialogEl: false,
4341     bodyEl:  false,
4342     footerEl:  false,
4343     titleEl:  false,
4344     closeEl:  false,
4345
4346     size: '',
4347     
4348     max_width: 0,
4349     
4350     max_height: 0,
4351     
4352     fit_content: false,
4353     editableTitle  : false,
4354
4355     onRender : function(ct, position)
4356     {
4357         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4358
4359         if(!this.el){
4360             var cfg = Roo.apply({},  this.getAutoCreate());
4361             cfg.id = Roo.id();
4362             //if(!cfg.name){
4363             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4364             //}
4365             //if (!cfg.name.length) {
4366             //    delete cfg.name;
4367            // }
4368             if (this.cls) {
4369                 cfg.cls += ' ' + this.cls;
4370             }
4371             if (this.style) {
4372                 cfg.style = this.style;
4373             }
4374             this.el = Roo.get(document.body).createChild(cfg, position);
4375         }
4376         //var type = this.el.dom.type;
4377
4378
4379         if(this.tabIndex !== undefined){
4380             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4381         }
4382
4383         this.dialogEl = this.el.select('.modal-dialog',true).first();
4384         this.bodyEl = this.el.select('.modal-body',true).first();
4385         this.closeEl = this.el.select('.modal-header .close', true).first();
4386         this.headerEl = this.el.select('.modal-header',true).first();
4387         this.titleEl = this.el.select('.modal-title',true).first();
4388         this.footerEl = this.el.select('.modal-footer',true).first();
4389
4390         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4391         
4392         //this.el.addClass("x-dlg-modal");
4393
4394         if (this.buttons.length) {
4395             Roo.each(this.buttons, function(bb) {
4396                 var b = Roo.apply({}, bb);
4397                 b.xns = b.xns || Roo.bootstrap;
4398                 b.xtype = b.xtype || 'Button';
4399                 if (typeof(b.listeners) == 'undefined') {
4400                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4401                 }
4402
4403                 var btn = Roo.factory(b);
4404
4405                 btn.render(this.getButtonContainer());
4406
4407             },this);
4408         }
4409         // render the children.
4410         var nitems = [];
4411
4412         if(typeof(this.items) != 'undefined'){
4413             var items = this.items;
4414             delete this.items;
4415
4416             for(var i =0;i < items.length;i++) {
4417                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4418             }
4419         }
4420
4421         this.items = nitems;
4422
4423         // where are these used - they used to be body/close/footer
4424
4425
4426         this.initEvents();
4427         //this.el.addClass([this.fieldClass, this.cls]);
4428
4429     },
4430
4431     getAutoCreate : function()
4432     {
4433         // we will default to modal-body-overflow - might need to remove or make optional later.
4434         var bdy = {
4435                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4436                 html : this.html || ''
4437         };
4438
4439         var title = {
4440             tag: 'h5',
4441             cls : 'modal-title',
4442             html : this.title
4443         };
4444
4445         if(this.specificTitle){ // WTF is this?
4446             title = this.title;
4447         }
4448
4449         var header = [];
4450         if (this.allow_close && Roo.bootstrap.version == 3) {
4451             header.push({
4452                 tag: 'button',
4453                 cls : 'close',
4454                 html : '&times'
4455             });
4456         }
4457
4458         header.push(title);
4459
4460         if (this.editableTitle) {
4461             header.push({
4462                 cls: 'form-control roo-editable-title d-none',
4463                 tag: 'input',
4464                 type: 'text'
4465             });
4466         }
4467         
4468         if (this.allow_close && Roo.bootstrap.version == 4) {
4469             header.push({
4470                 tag: 'button',
4471                 cls : 'close',
4472                 html : '&times'
4473             });
4474         }
4475         
4476         var size = '';
4477
4478         if(this.size.length){
4479             size = 'modal-' + this.size;
4480         }
4481         
4482         var footer = Roo.bootstrap.version == 3 ?
4483             {
4484                 cls : 'modal-footer',
4485                 cn : [
4486                     {
4487                         tag: 'div',
4488                         cls: 'btn-' + this.buttonPosition
4489                     }
4490                 ]
4491
4492             } :
4493             {  // BS4 uses mr-auto on left buttons....
4494                 cls : 'modal-footer'
4495             };
4496
4497             
4498
4499         
4500         
4501         var modal = {
4502             cls: "modal",
4503              cn : [
4504                 {
4505                     cls: "modal-dialog " + size,
4506                     cn : [
4507                         {
4508                             cls : "modal-content",
4509                             cn : [
4510                                 {
4511                                     cls : 'modal-header',
4512                                     cn : header
4513                                 },
4514                                 bdy,
4515                                 footer
4516                             ]
4517
4518                         }
4519                     ]
4520
4521                 }
4522             ]
4523         };
4524
4525         if(this.animate){
4526             modal.cls += ' fade';
4527         }
4528
4529         return modal;
4530
4531     },
4532     getChildContainer : function() {
4533
4534          return this.bodyEl;
4535
4536     },
4537     getButtonContainer : function() {
4538         
4539          return Roo.bootstrap.version == 4 ?
4540             this.el.select('.modal-footer',true).first()
4541             : this.el.select('.modal-footer div',true).first();
4542
4543     },
4544     initEvents : function()
4545     {
4546         if (this.allow_close) {
4547             this.closeEl.on('click', this.hide, this);
4548         }
4549         Roo.EventManager.onWindowResize(this.resize, this, true);
4550         if (this.editableTitle) {
4551             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4552             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4553             this.headerEditEl.on('keyup', function(e) {
4554                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4555                         this.toggleHeaderInput(false)
4556                     }
4557                 }, this);
4558             this.headerEditEl.on('blur', function(e) {
4559                 this.toggleHeaderInput(false)
4560             },this);
4561         }
4562
4563     },
4564   
4565
4566     resize : function()
4567     {
4568         this.maskEl.setSize(
4569             Roo.lib.Dom.getViewWidth(true),
4570             Roo.lib.Dom.getViewHeight(true)
4571         );
4572         
4573         if (this.fitwindow) {
4574             
4575            this.dialogEl.setStyle( { 'max-width' : '100%' });
4576             this.setSize(
4577                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4578                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579             );
4580             return;
4581         }
4582         
4583         if(this.max_width !== 0) {
4584             
4585             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4586             
4587             if(this.height) {
4588                 this.setSize(w, this.height);
4589                 return;
4590             }
4591             
4592             if(this.max_height) {
4593                 this.setSize(w,Math.min(
4594                     this.max_height,
4595                     Roo.lib.Dom.getViewportHeight(true) - 60
4596                 ));
4597                 
4598                 return;
4599             }
4600             
4601             if(!this.fit_content) {
4602                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4603                 return;
4604             }
4605             
4606             this.setSize(w, Math.min(
4607                 60 +
4608                 this.headerEl.getHeight() + 
4609                 this.footerEl.getHeight() + 
4610                 this.getChildHeight(this.bodyEl.dom.childNodes),
4611                 Roo.lib.Dom.getViewportHeight(true) - 60)
4612             );
4613         }
4614         
4615     },
4616
4617     setSize : function(w,h)
4618     {
4619         if (!w && !h) {
4620             return;
4621         }
4622         
4623         this.resizeTo(w,h);
4624     },
4625
4626     show : function() {
4627
4628         if (!this.rendered) {
4629             this.render();
4630         }
4631         this.toggleHeaderInput(false);
4632         //this.el.setStyle('display', 'block');
4633         this.el.removeClass('hideing');
4634         this.el.dom.style.display='block';
4635         
4636         Roo.get(document.body).addClass('modal-open');
4637  
4638         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4639             
4640             (function(){
4641                 this.el.addClass('show');
4642                 this.el.addClass('in');
4643             }).defer(50, this);
4644         }else{
4645             this.el.addClass('show');
4646             this.el.addClass('in');
4647         }
4648
4649         // not sure how we can show data in here..
4650         //if (this.tmpl) {
4651         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4652         //}
4653
4654         Roo.get(document.body).addClass("x-body-masked");
4655         
4656         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4657         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4658         this.maskEl.dom.style.display = 'block';
4659         this.maskEl.addClass('show');
4660         
4661         
4662         this.resize();
4663         
4664         this.fireEvent('show', this);
4665
4666         // set zindex here - otherwise it appears to be ignored...
4667         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4668
4669         (function () {
4670             this.items.forEach( function(e) {
4671                 e.layout ? e.layout() : false;
4672
4673             });
4674         }).defer(100,this);
4675
4676     },
4677     hide : function()
4678     {
4679         if(this.fireEvent("beforehide", this) !== false){
4680             
4681             this.maskEl.removeClass('show');
4682             
4683             this.maskEl.dom.style.display = '';
4684             Roo.get(document.body).removeClass("x-body-masked");
4685             this.el.removeClass('in');
4686             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4687
4688             if(this.animate){ // why
4689                 this.el.addClass('hideing');
4690                 this.el.removeClass('show');
4691                 (function(){
4692                     if (!this.el.hasClass('hideing')) {
4693                         return; // it's been shown again...
4694                     }
4695                     
4696                     this.el.dom.style.display='';
4697
4698                     Roo.get(document.body).removeClass('modal-open');
4699                     this.el.removeClass('hideing');
4700                 }).defer(150,this);
4701                 
4702             }else{
4703                 this.el.removeClass('show');
4704                 this.el.dom.style.display='';
4705                 Roo.get(document.body).removeClass('modal-open');
4706
4707             }
4708             this.fireEvent('hide', this);
4709         }
4710     },
4711     isVisible : function()
4712     {
4713         
4714         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4715         
4716     },
4717
4718     addButton : function(str, cb)
4719     {
4720
4721
4722         var b = Roo.apply({}, { html : str } );
4723         b.xns = b.xns || Roo.bootstrap;
4724         b.xtype = b.xtype || 'Button';
4725         if (typeof(b.listeners) == 'undefined') {
4726             b.listeners = { click : cb.createDelegate(this)  };
4727         }
4728
4729         var btn = Roo.factory(b);
4730
4731         btn.render(this.getButtonContainer());
4732
4733         return btn;
4734
4735     },
4736
4737     setDefaultButton : function(btn)
4738     {
4739         //this.el.select('.modal-footer').()
4740     },
4741
4742     resizeTo: function(w,h)
4743     {
4744         this.dialogEl.setWidth(w);
4745         
4746         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4747
4748         this.bodyEl.setHeight(h - diff);
4749         
4750         this.fireEvent('resize', this);
4751     },
4752     
4753     setContentSize  : function(w, h)
4754     {
4755
4756     },
4757     onButtonClick: function(btn,e)
4758     {
4759         //Roo.log([a,b,c]);
4760         this.fireEvent('btnclick', btn.name, e);
4761     },
4762      /**
4763      * Set the title of the Dialog
4764      * @param {String} str new Title
4765      */
4766     setTitle: function(str) {
4767         this.titleEl.dom.innerHTML = str;
4768         this.title = str;
4769     },
4770     /**
4771      * Set the body of the Dialog
4772      * @param {String} str new Title
4773      */
4774     setBody: function(str) {
4775         this.bodyEl.dom.innerHTML = str;
4776     },
4777     /**
4778      * Set the body of the Dialog using the template
4779      * @param {Obj} data - apply this data to the template and replace the body contents.
4780      */
4781     applyBody: function(obj)
4782     {
4783         if (!this.tmpl) {
4784             Roo.log("Error - using apply Body without a template");
4785             //code
4786         }
4787         this.tmpl.overwrite(this.bodyEl, obj);
4788     },
4789     
4790     getChildHeight : function(child_nodes)
4791     {
4792         if(
4793             !child_nodes ||
4794             child_nodes.length == 0
4795         ) {
4796             return 0;
4797         }
4798         
4799         var child_height = 0;
4800         
4801         for(var i = 0; i < child_nodes.length; i++) {
4802             
4803             /*
4804             * for modal with tabs...
4805             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4806                 
4807                 var layout_childs = child_nodes[i].childNodes;
4808                 
4809                 for(var j = 0; j < layout_childs.length; j++) {
4810                     
4811                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4812                         
4813                         var layout_body_childs = layout_childs[j].childNodes;
4814                         
4815                         for(var k = 0; k < layout_body_childs.length; k++) {
4816                             
4817                             if(layout_body_childs[k].classList.contains('navbar')) {
4818                                 child_height += layout_body_childs[k].offsetHeight;
4819                                 continue;
4820                             }
4821                             
4822                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4823                                 
4824                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4825                                 
4826                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4827                                     
4828                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4829                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4830                                         continue;
4831                                     }
4832                                     
4833                                 }
4834                                 
4835                             }
4836                             
4837                         }
4838                     }
4839                 }
4840                 continue;
4841             }
4842             */
4843             
4844             child_height += child_nodes[i].offsetHeight;
4845             // Roo.log(child_nodes[i].offsetHeight);
4846         }
4847         
4848         return child_height;
4849     },
4850     toggleHeaderInput : function(is_edit)
4851     {
4852         if (!this.editableTitle) {
4853             return; // not editable.
4854         }
4855         if (is_edit && this.is_header_editing) {
4856             return; // already editing..
4857         }
4858         if (is_edit) {
4859     
4860             this.headerEditEl.dom.value = this.title;
4861             this.headerEditEl.removeClass('d-none');
4862             this.headerEditEl.dom.focus();
4863             this.titleEl.addClass('d-none');
4864             
4865             this.is_header_editing = true;
4866             return
4867         }
4868         // flip back to not editing.
4869         this.title = this.headerEditEl.dom.value;
4870         this.headerEditEl.addClass('d-none');
4871         this.titleEl.removeClass('d-none');
4872         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4873         this.is_header_editing = false;
4874         this.fireEvent('titlechanged', this, this.title);
4875     
4876             
4877         
4878     }
4879
4880 });
4881
4882
4883 Roo.apply(Roo.bootstrap.Modal,  {
4884     /**
4885          * Button config that displays a single OK button
4886          * @type Object
4887          */
4888         OK :  [{
4889             name : 'ok',
4890             weight : 'primary',
4891             html : 'OK'
4892         }],
4893         /**
4894          * Button config that displays Yes and No buttons
4895          * @type Object
4896          */
4897         YESNO : [
4898             {
4899                 name  : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name  :'yes',
4904                 weight : 'primary',
4905                 html : 'Yes'
4906             }
4907         ],
4908
4909         /**
4910          * Button config that displays OK and Cancel buttons
4911          * @type Object
4912          */
4913         OKCANCEL : [
4914             {
4915                name : 'cancel',
4916                 html : 'Cancel'
4917             },
4918             {
4919                 name : 'ok',
4920                 weight : 'primary',
4921                 html : 'OK'
4922             }
4923         ],
4924         /**
4925          * Button config that displays Yes, No and Cancel buttons
4926          * @type Object
4927          */
4928         YESNOCANCEL : [
4929             {
4930                 name : 'yes',
4931                 weight : 'primary',
4932                 html : 'Yes'
4933             },
4934             {
4935                 name : 'no',
4936                 html : 'No'
4937             },
4938             {
4939                 name : 'cancel',
4940                 html : 'Cancel'
4941             }
4942         ],
4943         
4944         zIndex : 10001
4945 });
4946
4947 /*
4948  * - LGPL
4949  *
4950  * messagebox - can be used as a replace
4951  * 
4952  */
4953 /**
4954  * @class Roo.MessageBox
4955  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4956  * Example usage:
4957  *<pre><code>
4958 // Basic alert:
4959 Roo.Msg.alert('Status', 'Changes saved successfully.');
4960
4961 // Prompt for user data:
4962 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4963     if (btn == 'ok'){
4964         // process text value...
4965     }
4966 });
4967
4968 // Show a dialog using config options:
4969 Roo.Msg.show({
4970    title:'Save Changes?',
4971    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4972    buttons: Roo.Msg.YESNOCANCEL,
4973    fn: processResult,
4974    animEl: 'elId'
4975 });
4976 </code></pre>
4977  * @singleton
4978  */
4979 Roo.bootstrap.MessageBox = function(){
4980     var dlg, opt, mask, waitTimer;
4981     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4982     var buttons, activeTextEl, bwidth;
4983
4984     
4985     // private
4986     var handleButton = function(button){
4987         dlg.hide();
4988         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4989     };
4990
4991     // private
4992     var handleHide = function(){
4993         if(opt && opt.cls){
4994             dlg.el.removeClass(opt.cls);
4995         }
4996         //if(waitTimer){
4997         //    Roo.TaskMgr.stop(waitTimer);
4998         //    waitTimer = null;
4999         //}
5000     };
5001
5002     // private
5003     var updateButtons = function(b){
5004         var width = 0;
5005         if(!b){
5006             buttons["ok"].hide();
5007             buttons["cancel"].hide();
5008             buttons["yes"].hide();
5009             buttons["no"].hide();
5010             dlg.footerEl.hide();
5011             
5012             return width;
5013         }
5014         dlg.footerEl.show();
5015         for(var k in buttons){
5016             if(typeof buttons[k] != "function"){
5017                 if(b[k]){
5018                     buttons[k].show();
5019                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5020                     width += buttons[k].el.getWidth()+15;
5021                 }else{
5022                     buttons[k].hide();
5023                 }
5024             }
5025         }
5026         return width;
5027     };
5028
5029     // private
5030     var handleEsc = function(d, k, e){
5031         if(opt && opt.closable !== false){
5032             dlg.hide();
5033         }
5034         if(e){
5035             e.stopEvent();
5036         }
5037     };
5038
5039     return {
5040         /**
5041          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5042          * @return {Roo.BasicDialog} The BasicDialog element
5043          */
5044         getDialog : function(){
5045            if(!dlg){
5046                 dlg = new Roo.bootstrap.Modal( {
5047                     //draggable: true,
5048                     //resizable:false,
5049                     //constraintoviewport:false,
5050                     //fixedcenter:true,
5051                     //collapsible : false,
5052                     //shim:true,
5053                     //modal: true,
5054                 //    width: 'auto',
5055                   //  height:100,
5056                     //buttonAlign:"center",
5057                     closeClick : function(){
5058                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5059                             handleButton("no");
5060                         }else{
5061                             handleButton("cancel");
5062                         }
5063                     }
5064                 });
5065                 dlg.render();
5066                 dlg.on("hide", handleHide);
5067                 mask = dlg.mask;
5068                 //dlg.addKeyListener(27, handleEsc);
5069                 buttons = {};
5070                 this.buttons = buttons;
5071                 var bt = this.buttonText;
5072                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5073                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5074                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5075                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5076                 //Roo.log(buttons);
5077                 bodyEl = dlg.bodyEl.createChild({
5078
5079                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5080                         '<textarea class="roo-mb-textarea"></textarea>' +
5081                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5082                 });
5083                 msgEl = bodyEl.dom.firstChild;
5084                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5085                 textboxEl.enableDisplayMode();
5086                 textboxEl.addKeyListener([10,13], function(){
5087                     if(dlg.isVisible() && opt && opt.buttons){
5088                         if(opt.buttons.ok){
5089                             handleButton("ok");
5090                         }else if(opt.buttons.yes){
5091                             handleButton("yes");
5092                         }
5093                     }
5094                 });
5095                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5096                 textareaEl.enableDisplayMode();
5097                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5098                 progressEl.enableDisplayMode();
5099                 
5100                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5101                 var pf = progressEl.dom.firstChild;
5102                 if (pf) {
5103                     pp = Roo.get(pf.firstChild);
5104                     pp.setHeight(pf.offsetHeight);
5105                 }
5106                 
5107             }
5108             return dlg;
5109         },
5110
5111         /**
5112          * Updates the message box body text
5113          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5114          * the XHTML-compliant non-breaking space character '&amp;#160;')
5115          * @return {Roo.MessageBox} This message box
5116          */
5117         updateText : function(text)
5118         {
5119             if(!dlg.isVisible() && !opt.width){
5120                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5121                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5122             }
5123             msgEl.innerHTML = text || '&#160;';
5124       
5125             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5126             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5127             var w = Math.max(
5128                     Math.min(opt.width || cw , this.maxWidth), 
5129                     Math.max(opt.minWidth || this.minWidth, bwidth)
5130             );
5131             if(opt.prompt){
5132                 activeTextEl.setWidth(w);
5133             }
5134             if(dlg.isVisible()){
5135                 dlg.fixedcenter = false;
5136             }
5137             // to big, make it scroll. = But as usual stupid IE does not support
5138             // !important..
5139             
5140             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5141                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5142                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5143             } else {
5144                 bodyEl.dom.style.height = '';
5145                 bodyEl.dom.style.overflowY = '';
5146             }
5147             if (cw > w) {
5148                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5149             } else {
5150                 bodyEl.dom.style.overflowX = '';
5151             }
5152             
5153             dlg.setContentSize(w, bodyEl.getHeight());
5154             if(dlg.isVisible()){
5155                 dlg.fixedcenter = true;
5156             }
5157             return this;
5158         },
5159
5160         /**
5161          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5162          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5163          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5164          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5165          * @return {Roo.MessageBox} This message box
5166          */
5167         updateProgress : function(value, text){
5168             if(text){
5169                 this.updateText(text);
5170             }
5171             
5172             if (pp) { // weird bug on my firefox - for some reason this is not defined
5173                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5174                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5175             }
5176             return this;
5177         },        
5178
5179         /**
5180          * Returns true if the message box is currently displayed
5181          * @return {Boolean} True if the message box is visible, else false
5182          */
5183         isVisible : function(){
5184             return dlg && dlg.isVisible();  
5185         },
5186
5187         /**
5188          * Hides the message box if it is displayed
5189          */
5190         hide : function(){
5191             if(this.isVisible()){
5192                 dlg.hide();
5193             }  
5194         },
5195
5196         /**
5197          * Displays a new message box, or reinitializes an existing message box, based on the config options
5198          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5199          * The following config object properties are supported:
5200          * <pre>
5201 Property    Type             Description
5202 ----------  ---------------  ------------------------------------------------------------------------------------
5203 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5204                                    closes (defaults to undefined)
5205 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5206                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5207 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5208                                    progress and wait dialogs will ignore this property and always hide the
5209                                    close button as they can only be closed programmatically.
5210 cls               String           A custom CSS class to apply to the message box element
5211 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5212                                    displayed (defaults to 75)
5213 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5214                                    function will be btn (the name of the button that was clicked, if applicable,
5215                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5216                                    Progress and wait dialogs will ignore this option since they do not respond to
5217                                    user actions and can only be closed programmatically, so any required function
5218                                    should be called by the same code after it closes the dialog.
5219 icon              String           A CSS class that provides a background image to be used as an icon for
5220                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5221 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5222 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5223 modal             Boolean          False to allow user interaction with the page while the message box is
5224                                    displayed (defaults to true)
5225 msg               String           A string that will replace the existing message box body text (defaults
5226                                    to the XHTML-compliant non-breaking space character '&#160;')
5227 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5228 progress          Boolean          True to display a progress bar (defaults to false)
5229 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5230 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5231 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5232 title             String           The title text
5233 value             String           The string value to set into the active textbox element if displayed
5234 wait              Boolean          True to display a progress bar (defaults to false)
5235 width             Number           The width of the dialog in pixels
5236 </pre>
5237          *
5238          * Example usage:
5239          * <pre><code>
5240 Roo.Msg.show({
5241    title: 'Address',
5242    msg: 'Please enter your address:',
5243    width: 300,
5244    buttons: Roo.MessageBox.OKCANCEL,
5245    multiline: true,
5246    fn: saveAddress,
5247    animEl: 'addAddressBtn'
5248 });
5249 </code></pre>
5250          * @param {Object} config Configuration options
5251          * @return {Roo.MessageBox} This message box
5252          */
5253         show : function(options)
5254         {
5255             
5256             // this causes nightmares if you show one dialog after another
5257             // especially on callbacks..
5258              
5259             if(this.isVisible()){
5260                 
5261                 this.hide();
5262                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5263                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5264                 Roo.log("New Dialog Message:" +  options.msg )
5265                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5266                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5267                 
5268             }
5269             var d = this.getDialog();
5270             opt = options;
5271             d.setTitle(opt.title || "&#160;");
5272             d.closeEl.setDisplayed(opt.closable !== false);
5273             activeTextEl = textboxEl;
5274             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275             if(opt.prompt){
5276                 if(opt.multiline){
5277                     textboxEl.hide();
5278                     textareaEl.show();
5279                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5280                         opt.multiline : this.defaultTextHeight);
5281                     activeTextEl = textareaEl;
5282                 }else{
5283                     textboxEl.show();
5284                     textareaEl.hide();
5285                 }
5286             }else{
5287                 textboxEl.hide();
5288                 textareaEl.hide();
5289             }
5290             progressEl.setDisplayed(opt.progress === true);
5291             if (opt.progress) {
5292                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5293             }
5294             this.updateProgress(0);
5295             activeTextEl.dom.value = opt.value || "";
5296             if(opt.prompt){
5297                 dlg.setDefaultButton(activeTextEl);
5298             }else{
5299                 var bs = opt.buttons;
5300                 var db = null;
5301                 if(bs && bs.ok){
5302                     db = buttons["ok"];
5303                 }else if(bs && bs.yes){
5304                     db = buttons["yes"];
5305                 }
5306                 dlg.setDefaultButton(db);
5307             }
5308             bwidth = updateButtons(opt.buttons);
5309             this.updateText(opt.msg);
5310             if(opt.cls){
5311                 d.el.addClass(opt.cls);
5312             }
5313             d.proxyDrag = opt.proxyDrag === true;
5314             d.modal = opt.modal !== false;
5315             d.mask = opt.modal !== false ? mask : false;
5316             if(!d.isVisible()){
5317                 // force it to the end of the z-index stack so it gets a cursor in FF
5318                 document.body.appendChild(dlg.el.dom);
5319                 d.animateTarget = null;
5320                 d.show(options.animEl);
5321             }
5322             return this;
5323         },
5324
5325         /**
5326          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5327          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5328          * and closing the message box when the process is complete.
5329          * @param {String} title The title bar text
5330          * @param {String} msg The message box body text
5331          * @return {Roo.MessageBox} This message box
5332          */
5333         progress : function(title, msg){
5334             this.show({
5335                 title : title,
5336                 msg : msg,
5337                 buttons: false,
5338                 progress:true,
5339                 closable:false,
5340                 minWidth: this.minProgressWidth,
5341                 modal : true
5342             });
5343             return this;
5344         },
5345
5346         /**
5347          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5348          * If a callback function is passed it will be called after the user clicks the button, and the
5349          * id of the button that was clicked will be passed as the only parameter to the callback
5350          * (could also be the top-right close button).
5351          * @param {String} title The title bar text
5352          * @param {String} msg The message box body text
5353          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5354          * @param {Object} scope (optional) The scope of the callback function
5355          * @return {Roo.MessageBox} This message box
5356          */
5357         alert : function(title, msg, fn, scope)
5358         {
5359             this.show({
5360                 title : title,
5361                 msg : msg,
5362                 buttons: this.OK,
5363                 fn: fn,
5364                 closable : false,
5365                 scope : scope,
5366                 modal : true
5367             });
5368             return this;
5369         },
5370
5371         /**
5372          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5373          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5374          * You are responsible for closing the message box when the process is complete.
5375          * @param {String} msg The message box body text
5376          * @param {String} title (optional) The title bar text
5377          * @return {Roo.MessageBox} This message box
5378          */
5379         wait : function(msg, title){
5380             this.show({
5381                 title : title,
5382                 msg : msg,
5383                 buttons: false,
5384                 closable:false,
5385                 progress:true,
5386                 modal:true,
5387                 width:300,
5388                 wait:true
5389             });
5390             waitTimer = Roo.TaskMgr.start({
5391                 run: function(i){
5392                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5393                 },
5394                 interval: 1000
5395             });
5396             return this;
5397         },
5398
5399         /**
5400          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5401          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5402          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5403          * @param {String} title The title bar text
5404          * @param {String} msg The message box body text
5405          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5406          * @param {Object} scope (optional) The scope of the callback function
5407          * @return {Roo.MessageBox} This message box
5408          */
5409         confirm : function(title, msg, fn, scope){
5410             this.show({
5411                 title : title,
5412                 msg : msg,
5413                 buttons: this.YESNO,
5414                 fn: fn,
5415                 scope : scope,
5416                 modal : true
5417             });
5418             return this;
5419         },
5420
5421         /**
5422          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5423          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5424          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5425          * (could also be the top-right close button) and the text that was entered will be passed as the two
5426          * parameters to the callback.
5427          * @param {String} title The title bar text
5428          * @param {String} msg The message box body text
5429          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5430          * @param {Object} scope (optional) The scope of the callback function
5431          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5432          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5433          * @return {Roo.MessageBox} This message box
5434          */
5435         prompt : function(title, msg, fn, scope, multiline){
5436             this.show({
5437                 title : title,
5438                 msg : msg,
5439                 buttons: this.OKCANCEL,
5440                 fn: fn,
5441                 minWidth:250,
5442                 scope : scope,
5443                 prompt:true,
5444                 multiline: multiline,
5445                 modal : true
5446             });
5447             return this;
5448         },
5449
5450         /**
5451          * Button config that displays a single OK button
5452          * @type Object
5453          */
5454         OK : {ok:true},
5455         /**
5456          * Button config that displays Yes and No buttons
5457          * @type Object
5458          */
5459         YESNO : {yes:true, no:true},
5460         /**
5461          * Button config that displays OK and Cancel buttons
5462          * @type Object
5463          */
5464         OKCANCEL : {ok:true, cancel:true},
5465         /**
5466          * Button config that displays Yes, No and Cancel buttons
5467          * @type Object
5468          */
5469         YESNOCANCEL : {yes:true, no:true, cancel:true},
5470
5471         /**
5472          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5473          * @type Number
5474          */
5475         defaultTextHeight : 75,
5476         /**
5477          * The maximum width in pixels of the message box (defaults to 600)
5478          * @type Number
5479          */
5480         maxWidth : 600,
5481         /**
5482          * The minimum width in pixels of the message box (defaults to 100)
5483          * @type Number
5484          */
5485         minWidth : 100,
5486         /**
5487          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5488          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5489          * @type Number
5490          */
5491         minProgressWidth : 250,
5492         /**
5493          * An object containing the default button text strings that can be overriden for localized language support.
5494          * Supported properties are: ok, cancel, yes and no.
5495          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5496          * @type Object
5497          */
5498         buttonText : {
5499             ok : "OK",
5500             cancel : "Cancel",
5501             yes : "Yes",
5502             no : "No"
5503         }
5504     };
5505 }();
5506
5507 /**
5508  * Shorthand for {@link Roo.MessageBox}
5509  */
5510 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5511 Roo.Msg = Roo.Msg || Roo.MessageBox;
5512 /*
5513  * - LGPL
5514  *
5515  * navbar
5516  * 
5517  */
5518
5519 /**
5520  * @class Roo.bootstrap.Navbar
5521  * @extends Roo.bootstrap.Component
5522  * Bootstrap Navbar class
5523
5524  * @constructor
5525  * Create a new Navbar
5526  * @param {Object} config The config object
5527  */
5528
5529
5530 Roo.bootstrap.Navbar = function(config){
5531     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5532     this.addEvents({
5533         // raw events
5534         /**
5535          * @event beforetoggle
5536          * Fire before toggle the menu
5537          * @param {Roo.EventObject} e
5538          */
5539         "beforetoggle" : true
5540     });
5541 };
5542
5543 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5544     
5545     
5546    
5547     // private
5548     navItems : false,
5549     loadMask : false,
5550     
5551     
5552     getAutoCreate : function(){
5553         
5554         
5555         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5556         
5557     },
5558     
5559     initEvents :function ()
5560     {
5561         //Roo.log(this.el.select('.navbar-toggle',true));
5562         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5563         
5564         var mark = {
5565             tag: "div",
5566             cls:"x-dlg-mask"
5567         };
5568         
5569         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5570         
5571         var size = this.el.getSize();
5572         this.maskEl.setSize(size.width, size.height);
5573         this.maskEl.enableDisplayMode("block");
5574         this.maskEl.hide();
5575         
5576         if(this.loadMask){
5577             this.maskEl.show();
5578         }
5579     },
5580     
5581     
5582     getChildContainer : function()
5583     {
5584         if (this.el && this.el.select('.collapse').getCount()) {
5585             return this.el.select('.collapse',true).first();
5586         }
5587         
5588         return this.el;
5589     },
5590     
5591     mask : function()
5592     {
5593         this.maskEl.show();
5594     },
5595     
5596     unmask : function()
5597     {
5598         this.maskEl.hide();
5599     },
5600     onToggle : function()
5601     {
5602         
5603         if(this.fireEvent('beforetoggle', this) === false){
5604             return;
5605         }
5606         var ce = this.el.select('.navbar-collapse',true).first();
5607       
5608         if (!ce.hasClass('show')) {
5609            this.expand();
5610         } else {
5611             this.collapse();
5612         }
5613         
5614         
5615     
5616     },
5617     /**
5618      * Expand the navbar pulldown 
5619      */
5620     expand : function ()
5621     {
5622        
5623         var ce = this.el.select('.navbar-collapse',true).first();
5624         if (ce.hasClass('collapsing')) {
5625             return;
5626         }
5627         ce.dom.style.height = '';
5628                // show it...
5629         ce.addClass('in'); // old...
5630         ce.removeClass('collapse');
5631         ce.addClass('show');
5632         var h = ce.getHeight();
5633         Roo.log(h);
5634         ce.removeClass('show');
5635         // at this point we should be able to see it..
5636         ce.addClass('collapsing');
5637         
5638         ce.setHeight(0); // resize it ...
5639         ce.on('transitionend', function() {
5640             //Roo.log('done transition');
5641             ce.removeClass('collapsing');
5642             ce.addClass('show');
5643             ce.removeClass('collapse');
5644
5645             ce.dom.style.height = '';
5646         }, this, { single: true} );
5647         ce.setHeight(h);
5648         ce.dom.scrollTop = 0;
5649     },
5650     /**
5651      * Collapse the navbar pulldown 
5652      */
5653     collapse : function()
5654     {
5655          var ce = this.el.select('.navbar-collapse',true).first();
5656        
5657         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5658             // it's collapsed or collapsing..
5659             return;
5660         }
5661         ce.removeClass('in'); // old...
5662         ce.setHeight(ce.getHeight());
5663         ce.removeClass('show');
5664         ce.addClass('collapsing');
5665         
5666         ce.on('transitionend', function() {
5667             ce.dom.style.height = '';
5668             ce.removeClass('collapsing');
5669             ce.addClass('collapse');
5670         }, this, { single: true} );
5671         ce.setHeight(0);
5672     }
5673     
5674     
5675     
5676 });
5677
5678
5679
5680  
5681
5682  /*
5683  * - LGPL
5684  *
5685  * navbar
5686  * 
5687  */
5688
5689 /**
5690  * @class Roo.bootstrap.NavSimplebar
5691  * @extends Roo.bootstrap.Navbar
5692  * Bootstrap Sidebar class
5693  *
5694  * @cfg {Boolean} inverse is inverted color
5695  * 
5696  * @cfg {String} type (nav | pills | tabs)
5697  * @cfg {Boolean} arrangement stacked | justified
5698  * @cfg {String} align (left | right) alignment
5699  * 
5700  * @cfg {Boolean} main (true|false) main nav bar? default false
5701  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5702  * 
5703  * @cfg {String} tag (header|footer|nav|div) default is nav 
5704
5705  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5706  * 
5707  * 
5708  * @constructor
5709  * Create a new Sidebar
5710  * @param {Object} config The config object
5711  */
5712
5713
5714 Roo.bootstrap.NavSimplebar = function(config){
5715     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5716 };
5717
5718 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5719     
5720     inverse: false,
5721     
5722     type: false,
5723     arrangement: '',
5724     align : false,
5725     
5726     weight : 'light',
5727     
5728     main : false,
5729     
5730     
5731     tag : false,
5732     
5733     
5734     getAutoCreate : function(){
5735         
5736         
5737         var cfg = {
5738             tag : this.tag || 'div',
5739             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5740         };
5741         if (['light','white'].indexOf(this.weight) > -1) {
5742             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5743         }
5744         cfg.cls += ' bg-' + this.weight;
5745         
5746         if (this.inverse) {
5747             cfg.cls += ' navbar-inverse';
5748             
5749         }
5750         
5751         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5752         
5753         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5754             return cfg;
5755         }
5756         
5757         
5758     
5759         
5760         cfg.cn = [
5761             {
5762                 cls: 'nav nav-' + this.xtype,
5763                 tag : 'ul'
5764             }
5765         ];
5766         
5767          
5768         this.type = this.type || 'nav';
5769         if (['tabs','pills'].indexOf(this.type) != -1) {
5770             cfg.cn[0].cls += ' nav-' + this.type
5771         
5772         
5773         } else {
5774             if (this.type!=='nav') {
5775                 Roo.log('nav type must be nav/tabs/pills')
5776             }
5777             cfg.cn[0].cls += ' navbar-nav'
5778         }
5779         
5780         
5781         
5782         
5783         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5784             cfg.cn[0].cls += ' nav-' + this.arrangement;
5785         }
5786         
5787         
5788         if (this.align === 'right') {
5789             cfg.cn[0].cls += ' navbar-right';
5790         }
5791         
5792         
5793         
5794         
5795         return cfg;
5796     
5797         
5798     }
5799     
5800     
5801     
5802 });
5803
5804
5805
5806  
5807
5808  
5809        /*
5810  * - LGPL
5811  *
5812  * navbar
5813  * navbar-fixed-top
5814  * navbar-expand-md  fixed-top 
5815  */
5816
5817 /**
5818  * @class Roo.bootstrap.NavHeaderbar
5819  * @extends Roo.bootstrap.NavSimplebar
5820  * Bootstrap Sidebar class
5821  *
5822  * @cfg {String} brand what is brand
5823  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5824  * @cfg {String} brand_href href of the brand
5825  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5826  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5827  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5828  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5829  * 
5830  * @constructor
5831  * Create a new Sidebar
5832  * @param {Object} config The config object
5833  */
5834
5835
5836 Roo.bootstrap.NavHeaderbar = function(config){
5837     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5838       
5839 };
5840
5841 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5842     
5843     position: '',
5844     brand: '',
5845     brand_href: false,
5846     srButton : true,
5847     autohide : false,
5848     desktopCenter : false,
5849    
5850     
5851     getAutoCreate : function(){
5852         
5853         var   cfg = {
5854             tag: this.nav || 'nav',
5855             cls: 'navbar navbar-expand-md',
5856             role: 'navigation',
5857             cn: []
5858         };
5859         
5860         var cn = cfg.cn;
5861         if (this.desktopCenter) {
5862             cn.push({cls : 'container', cn : []});
5863             cn = cn[0].cn;
5864         }
5865         
5866         if(this.srButton){
5867             var btn = {
5868                 tag: 'button',
5869                 type: 'button',
5870                 cls: 'navbar-toggle navbar-toggler',
5871                 'data-toggle': 'collapse',
5872                 cn: [
5873                     {
5874                         tag: 'span',
5875                         cls: 'sr-only',
5876                         html: 'Toggle navigation'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar navbar-toggler-icon'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     },
5886                     {
5887                         tag: 'span',
5888                         cls: 'icon-bar'
5889                     }
5890                 ]
5891             };
5892             
5893             cn.push( Roo.bootstrap.version == 4 ? btn : {
5894                 tag: 'div',
5895                 cls: 'navbar-header',
5896                 cn: [
5897                     btn
5898                 ]
5899             });
5900         }
5901         
5902         cn.push({
5903             tag: 'div',
5904             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5905             cn : []
5906         });
5907         
5908         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5909         
5910         if (['light','white'].indexOf(this.weight) > -1) {
5911             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5912         }
5913         cfg.cls += ' bg-' + this.weight;
5914         
5915         
5916         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5917             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5918             
5919             // tag can override this..
5920             
5921             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5922         }
5923         
5924         if (this.brand !== '') {
5925             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5926             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5927                 tag: 'a',
5928                 href: this.brand_href ? this.brand_href : '#',
5929                 cls: 'navbar-brand',
5930                 cn: [
5931                 this.brand
5932                 ]
5933             });
5934         }
5935         
5936         if(this.main){
5937             cfg.cls += ' main-nav';
5938         }
5939         
5940         
5941         return cfg;
5942
5943         
5944     },
5945     getHeaderChildContainer : function()
5946     {
5947         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5948             return this.el.select('.navbar-header',true).first();
5949         }
5950         
5951         return this.getChildContainer();
5952     },
5953     
5954     getChildContainer : function()
5955     {
5956          
5957         return this.el.select('.roo-navbar-collapse',true).first();
5958          
5959         
5960     },
5961     
5962     initEvents : function()
5963     {
5964         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5965         
5966         if (this.autohide) {
5967             
5968             var prevScroll = 0;
5969             var ft = this.el;
5970             
5971             Roo.get(document).on('scroll',function(e) {
5972                 var ns = Roo.get(document).getScroll().top;
5973                 var os = prevScroll;
5974                 prevScroll = ns;
5975                 
5976                 if(ns > os){
5977                     ft.removeClass('slideDown');
5978                     ft.addClass('slideUp');
5979                     return;
5980                 }
5981                 ft.removeClass('slideUp');
5982                 ft.addClass('slideDown');
5983                  
5984               
5985           },this);
5986         }
5987     }    
5988     
5989 });
5990
5991
5992
5993  
5994
5995  /*
5996  * - LGPL
5997  *
5998  * navbar
5999  * 
6000  */
6001
6002 /**
6003  * @class Roo.bootstrap.NavSidebar
6004  * @extends Roo.bootstrap.Navbar
6005  * Bootstrap Sidebar class
6006  * 
6007  * @constructor
6008  * Create a new Sidebar
6009  * @param {Object} config The config object
6010  */
6011
6012
6013 Roo.bootstrap.NavSidebar = function(config){
6014     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6015 };
6016
6017 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6018     
6019     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6020     
6021     getAutoCreate : function(){
6022         
6023         
6024         return  {
6025             tag: 'div',
6026             cls: 'sidebar sidebar-nav'
6027         };
6028     
6029         
6030     }
6031     
6032     
6033     
6034 });
6035
6036
6037
6038  
6039
6040  /*
6041  * - LGPL
6042  *
6043  * nav group
6044  * 
6045  */
6046
6047 /**
6048  * @class Roo.bootstrap.NavGroup
6049  * @extends Roo.bootstrap.Component
6050  * Bootstrap NavGroup class
6051  * @cfg {String} align (left|right)
6052  * @cfg {Boolean} inverse
6053  * @cfg {String} type (nav|pills|tab) default nav
6054  * @cfg {String} navId - reference Id for navbar.
6055  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6056  * 
6057  * @constructor
6058  * Create a new nav group
6059  * @param {Object} config The config object
6060  */
6061
6062 Roo.bootstrap.NavGroup = function(config){
6063     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6064     this.navItems = [];
6065    
6066     Roo.bootstrap.NavGroup.register(this);
6067      this.addEvents({
6068         /**
6069              * @event changed
6070              * Fires when the active item changes
6071              * @param {Roo.bootstrap.NavGroup} this
6072              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6073              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6074          */
6075         'changed': true
6076      });
6077     
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6081     
6082     align: '',
6083     inverse: false,
6084     form: false,
6085     type: 'nav',
6086     navId : '',
6087     // private
6088     pilltype : true,
6089     
6090     navItems : false, 
6091     
6092     getAutoCreate : function()
6093     {
6094         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6095         
6096         cfg = {
6097             tag : 'ul',
6098             cls: 'nav' 
6099         };
6100         if (Roo.bootstrap.version == 4) {
6101             if (['tabs','pills'].indexOf(this.type) != -1) {
6102                 cfg.cls += ' nav-' + this.type; 
6103             } else {
6104                 // trying to remove so header bar can right align top?
6105                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6106                     // do not use on header bar... 
6107                     cfg.cls += ' navbar-nav';
6108                 }
6109             }
6110             
6111         } else {
6112             if (['tabs','pills'].indexOf(this.type) != -1) {
6113                 cfg.cls += ' nav-' + this.type
6114             } else {
6115                 if (this.type !== 'nav') {
6116                     Roo.log('nav type must be nav/tabs/pills')
6117                 }
6118                 cfg.cls += ' navbar-nav'
6119             }
6120         }
6121         
6122         if (this.parent() && this.parent().sidebar) {
6123             cfg = {
6124                 tag: 'ul',
6125                 cls: 'dashboard-menu sidebar-menu'
6126             };
6127             
6128             return cfg;
6129         }
6130         
6131         if (this.form === true) {
6132             cfg = {
6133                 tag: 'form',
6134                 cls: 'navbar-form form-inline'
6135             };
6136             //nav navbar-right ml-md-auto
6137             if (this.align === 'right') {
6138                 cfg.cls += ' navbar-right ml-md-auto';
6139             } else {
6140                 cfg.cls += ' navbar-left';
6141             }
6142         }
6143         
6144         if (this.align === 'right') {
6145             cfg.cls += ' navbar-right ml-md-auto';
6146         } else {
6147             cfg.cls += ' mr-auto';
6148         }
6149         
6150         if (this.inverse) {
6151             cfg.cls += ' navbar-inverse';
6152             
6153         }
6154         
6155         
6156         return cfg;
6157     },
6158     /**
6159     * sets the active Navigation item
6160     * @param {Roo.bootstrap.NavItem} the new current navitem
6161     */
6162     setActiveItem : function(item)
6163     {
6164         var prev = false;
6165         Roo.each(this.navItems, function(v){
6166             if (v == item) {
6167                 return ;
6168             }
6169             if (v.isActive()) {
6170                 v.setActive(false, true);
6171                 prev = v;
6172                 
6173             }
6174             
6175         });
6176
6177         item.setActive(true, true);
6178         this.fireEvent('changed', this, item, prev);
6179         
6180         
6181     },
6182     /**
6183     * gets the active Navigation item
6184     * @return {Roo.bootstrap.NavItem} the current navitem
6185     */
6186     getActive : function()
6187     {
6188         
6189         var prev = false;
6190         Roo.each(this.navItems, function(v){
6191             
6192             if (v.isActive()) {
6193                 prev = v;
6194                 
6195             }
6196             
6197         });
6198         return prev;
6199     },
6200     
6201     indexOfNav : function()
6202     {
6203         
6204         var prev = false;
6205         Roo.each(this.navItems, function(v,i){
6206             
6207             if (v.isActive()) {
6208                 prev = i;
6209                 
6210             }
6211             
6212         });
6213         return prev;
6214     },
6215     /**
6216     * adds a Navigation item
6217     * @param {Roo.bootstrap.NavItem} the navitem to add
6218     */
6219     addItem : function(cfg)
6220     {
6221         if (this.form && Roo.bootstrap.version == 4) {
6222             cfg.tag = 'div';
6223         }
6224         var cn = new Roo.bootstrap.NavItem(cfg);
6225         this.register(cn);
6226         cn.parentId = this.id;
6227         cn.onRender(this.el, null);
6228         return cn;
6229     },
6230     /**
6231     * register a Navigation item
6232     * @param {Roo.bootstrap.NavItem} the navitem to add
6233     */
6234     register : function(item)
6235     {
6236         this.navItems.push( item);
6237         item.navId = this.navId;
6238     
6239     },
6240     
6241     /**
6242     * clear all the Navigation item
6243     */
6244    
6245     clearAll : function()
6246     {
6247         this.navItems = [];
6248         this.el.dom.innerHTML = '';
6249     },
6250     
6251     getNavItem: function(tabId)
6252     {
6253         var ret = false;
6254         Roo.each(this.navItems, function(e) {
6255             if (e.tabId == tabId) {
6256                ret =  e;
6257                return false;
6258             }
6259             return true;
6260             
6261         });
6262         return ret;
6263     },
6264     
6265     setActiveNext : function()
6266     {
6267         var i = this.indexOfNav(this.getActive());
6268         if (i > this.navItems.length) {
6269             return;
6270         }
6271         this.setActiveItem(this.navItems[i+1]);
6272     },
6273     setActivePrev : function()
6274     {
6275         var i = this.indexOfNav(this.getActive());
6276         if (i  < 1) {
6277             return;
6278         }
6279         this.setActiveItem(this.navItems[i-1]);
6280     },
6281     clearWasActive : function(except) {
6282         Roo.each(this.navItems, function(e) {
6283             if (e.tabId != except.tabId && e.was_active) {
6284                e.was_active = false;
6285                return false;
6286             }
6287             return true;
6288             
6289         });
6290     },
6291     getWasActive : function ()
6292     {
6293         var r = false;
6294         Roo.each(this.navItems, function(e) {
6295             if (e.was_active) {
6296                r = e;
6297                return false;
6298             }
6299             return true;
6300             
6301         });
6302         return r;
6303     }
6304     
6305     
6306 });
6307
6308  
6309 Roo.apply(Roo.bootstrap.NavGroup, {
6310     
6311     groups: {},
6312      /**
6313     * register a Navigation Group
6314     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6315     */
6316     register : function(navgrp)
6317     {
6318         this.groups[navgrp.navId] = navgrp;
6319         
6320     },
6321     /**
6322     * fetch a Navigation Group based on the navigation ID
6323     * @param {string} the navgroup to add
6324     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6325     */
6326     get: function(navId) {
6327         if (typeof(this.groups[navId]) == 'undefined') {
6328             return false;
6329             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6330         }
6331         return this.groups[navId] ;
6332     }
6333     
6334     
6335     
6336 });
6337
6338  /*
6339  * - LGPL
6340  *
6341  * row
6342  * 
6343  */
6344
6345 /**
6346  * @class Roo.bootstrap.NavItem
6347  * @extends Roo.bootstrap.Component
6348  * Bootstrap Navbar.NavItem class
6349  * @cfg {String} href  link to
6350  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6351  * @cfg {Boolean} button_outline show and outlined button
6352  * @cfg {String} html content of button
6353  * @cfg {String} badge text inside badge
6354  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6355  * @cfg {String} glyphicon DEPRICATED - use fa
6356  * @cfg {String} icon DEPRICATED - use fa
6357  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6358  * @cfg {Boolean} active Is item active
6359  * @cfg {Boolean} disabled Is item disabled
6360  * @cfg {String} linkcls  Link Class
6361  * @cfg {Boolean} preventDefault (true | false) default false
6362  * @cfg {String} tabId the tab that this item activates.
6363  * @cfg {String} tagtype (a|span) render as a href or span?
6364  * @cfg {Boolean} animateRef (true|false) link to element default false  
6365   
6366  * @constructor
6367  * Create a new Navbar Item
6368  * @param {Object} config The config object
6369  */
6370 Roo.bootstrap.NavItem = function(config){
6371     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372     this.addEvents({
6373         // raw events
6374         /**
6375          * @event click
6376          * The raw click event for the entire grid.
6377          * @param {Roo.EventObject} e
6378          */
6379         "click" : true,
6380          /**
6381             * @event changed
6382             * Fires when the active item active state changes
6383             * @param {Roo.bootstrap.NavItem} this
6384             * @param {boolean} state the new state
6385              
6386          */
6387         'changed': true,
6388         /**
6389             * @event scrollto
6390             * Fires when scroll to element
6391             * @param {Roo.bootstrap.NavItem} this
6392             * @param {Object} options
6393             * @param {Roo.EventObject} e
6394              
6395          */
6396         'scrollto': true
6397     });
6398    
6399 };
6400
6401 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6402     
6403     href: false,
6404     html: '',
6405     badge: '',
6406     icon: false,
6407     fa : false,
6408     glyphicon: false,
6409     active: false,
6410     preventDefault : false,
6411     tabId : false,
6412     tagtype : 'a',
6413     tag: 'li',
6414     disabled : false,
6415     animateRef : false,
6416     was_active : false,
6417     button_weight : '',
6418     button_outline : false,
6419     linkcls : '',
6420     navLink: false,
6421     
6422     getAutoCreate : function(){
6423          
6424         var cfg = {
6425             tag: this.tag,
6426             cls: 'nav-item'
6427         };
6428         
6429         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6430         
6431         if (this.active) {
6432             cfg.cls +=  ' active' ;
6433         }
6434         if (this.disabled) {
6435             cfg.cls += ' disabled';
6436         }
6437         
6438         // BS4 only?
6439         if (this.button_weight.length) {
6440             cfg.tag = this.href ? 'a' : 'button';
6441             cfg.html = this.html || '';
6442             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6443             if (this.href) {
6444                 cfg.href = this.href;
6445             }
6446             if (this.fa) {
6447                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6448             } else {
6449                 cfg.cls += " nav-html";
6450             }
6451             
6452             // menu .. should add dropdown-menu class - so no need for carat..
6453             
6454             if (this.badge !== '') {
6455                  
6456                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457             }
6458             return cfg;
6459         }
6460         
6461         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6462             cfg.cn = [
6463                 {
6464                     tag: this.tagtype,
6465                     href : this.href || "#",
6466                     html: this.html || '',
6467                     cls : ''
6468                 }
6469             ];
6470             if (this.tagtype == 'a') {
6471                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6472         
6473             }
6474             if (this.icon) {
6475                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6476             } else  if (this.fa) {
6477                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6478             } else if(this.glyphicon) {
6479                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6480             } else {
6481                 cfg.cn[0].cls += " nav-html";
6482             }
6483             
6484             if (this.menu) {
6485                 cfg.cn[0].html += " <span class='caret'></span>";
6486              
6487             }
6488             
6489             if (this.badge !== '') {
6490                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6491             }
6492         }
6493         
6494         
6495         
6496         return cfg;
6497     },
6498     onRender : function(ct, position)
6499     {
6500        // Roo.log("Call onRender: " + this.xtype);
6501         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6502             this.tag = 'div';
6503         }
6504         
6505         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6506         this.navLink = this.el.select('.nav-link',true).first();
6507         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508         return ret;
6509     },
6510       
6511     
6512     initEvents: function() 
6513     {
6514         if (typeof (this.menu) != 'undefined') {
6515             this.menu.parentType = this.xtype;
6516             this.menu.triggerEl = this.el;
6517             this.menu = this.addxtype(Roo.apply({}, this.menu));
6518         }
6519         
6520         this.el.on('click', this.onClick, this);
6521         
6522         //if(this.tagtype == 'span'){
6523         //    this.el.select('span',true).on('click', this.onClick, this);
6524         //}
6525        
6526         // at this point parent should be available..
6527         this.parent().register(this);
6528     },
6529     
6530     onClick : function(e)
6531     {
6532         if (e.getTarget('.dropdown-menu-item')) {
6533             // did you click on a menu itemm.... - then don't trigger onclick..
6534             return;
6535         }
6536         
6537         if(
6538                 this.preventDefault || 
6539                 this.href == '#' 
6540         ){
6541             Roo.log("NavItem - prevent Default?");
6542             e.preventDefault();
6543         }
6544         
6545         if (this.disabled) {
6546             return;
6547         }
6548         
6549         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6550         if (tg && tg.transition) {
6551             Roo.log("waiting for the transitionend");
6552             return;
6553         }
6554         
6555         
6556         
6557         //Roo.log("fire event clicked");
6558         if(this.fireEvent('click', this, e) === false){
6559             return;
6560         };
6561         
6562         if(this.tagtype == 'span'){
6563             return;
6564         }
6565         
6566         //Roo.log(this.href);
6567         var ael = this.el.select('a',true).first();
6568         //Roo.log(ael);
6569         
6570         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6571             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6572             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6573                 return; // ignore... - it's a 'hash' to another page.
6574             }
6575             Roo.log("NavItem - prevent Default?");
6576             e.preventDefault();
6577             this.scrollToElement(e);
6578         }
6579         
6580         
6581         var p =  this.parent();
6582    
6583         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6584             if (typeof(p.setActiveItem) !== 'undefined') {
6585                 p.setActiveItem(this);
6586             }
6587         }
6588         
6589         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6590         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6591             // remove the collapsed menu expand...
6592             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6593         }
6594     },
6595     
6596     isActive: function () {
6597         return this.active
6598     },
6599     setActive : function(state, fire, is_was_active)
6600     {
6601         if (this.active && !state && this.navId) {
6602             this.was_active = true;
6603             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6604             if (nv) {
6605                 nv.clearWasActive(this);
6606             }
6607             
6608         }
6609         this.active = state;
6610         
6611         if (!state ) {
6612             this.el.removeClass('active');
6613             this.navLink ? this.navLink.removeClass('active') : false;
6614         } else if (!this.el.hasClass('active')) {
6615             
6616             this.el.addClass('active');
6617             if (Roo.bootstrap.version == 4 && this.navLink ) {
6618                 this.navLink.addClass('active');
6619             }
6620             
6621         }
6622         if (fire) {
6623             this.fireEvent('changed', this, state);
6624         }
6625         
6626         // show a panel if it's registered and related..
6627         
6628         if (!this.navId || !this.tabId || !state || is_was_active) {
6629             return;
6630         }
6631         
6632         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6633         if (!tg) {
6634             return;
6635         }
6636         var pan = tg.getPanelByName(this.tabId);
6637         if (!pan) {
6638             return;
6639         }
6640         // if we can not flip to new panel - go back to old nav highlight..
6641         if (false == tg.showPanel(pan)) {
6642             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6643             if (nv) {
6644                 var onav = nv.getWasActive();
6645                 if (onav) {
6646                     onav.setActive(true, false, true);
6647                 }
6648             }
6649             
6650         }
6651         
6652         
6653         
6654     },
6655      // this should not be here...
6656     setDisabled : function(state)
6657     {
6658         this.disabled = state;
6659         if (!state ) {
6660             this.el.removeClass('disabled');
6661         } else if (!this.el.hasClass('disabled')) {
6662             this.el.addClass('disabled');
6663         }
6664         
6665     },
6666     
6667     /**
6668      * Fetch the element to display the tooltip on.
6669      * @return {Roo.Element} defaults to this.el
6670      */
6671     tooltipEl : function()
6672     {
6673         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6674     },
6675     
6676     scrollToElement : function(e)
6677     {
6678         var c = document.body;
6679         
6680         /*
6681          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6682          */
6683         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6684             c = document.documentElement;
6685         }
6686         
6687         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6688         
6689         if(!target){
6690             return;
6691         }
6692
6693         var o = target.calcOffsetsTo(c);
6694         
6695         var options = {
6696             target : target,
6697             value : o[1]
6698         };
6699         
6700         this.fireEvent('scrollto', this, options, e);
6701         
6702         Roo.get(c).scrollTo('top', options.value, true);
6703         
6704         return;
6705     },
6706     /**
6707      * Set the HTML (text content) of the item
6708      * @param {string} html  content for the nav item
6709      */
6710     setHtml : function(html)
6711     {
6712         this.html = html;
6713         this.htmlEl.dom.innerHTML = html;
6714         
6715     } 
6716 });
6717  
6718
6719  /*
6720  * - LGPL
6721  *
6722  * sidebar item
6723  *
6724  *  li
6725  *    <span> icon </span>
6726  *    <span> text </span>
6727  *    <span>badge </span>
6728  */
6729
6730 /**
6731  * @class Roo.bootstrap.NavSidebarItem
6732  * @extends Roo.bootstrap.NavItem
6733  * Bootstrap Navbar.NavSidebarItem class
6734  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6735  * {Boolean} open is the menu open
6736  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6737  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6738  * {String} buttonSize (sm|md|lg)the extra classes for the button
6739  * {Boolean} showArrow show arrow next to the text (default true)
6740  * @constructor
6741  * Create a new Navbar Button
6742  * @param {Object} config The config object
6743  */
6744 Roo.bootstrap.NavSidebarItem = function(config){
6745     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746     this.addEvents({
6747         // raw events
6748         /**
6749          * @event click
6750          * The raw click event for the entire grid.
6751          * @param {Roo.EventObject} e
6752          */
6753         "click" : true,
6754          /**
6755             * @event changed
6756             * Fires when the active item active state changes
6757             * @param {Roo.bootstrap.NavSidebarItem} this
6758             * @param {boolean} state the new state
6759              
6760          */
6761         'changed': true
6762     });
6763    
6764 };
6765
6766 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6767     
6768     badgeWeight : 'default',
6769     
6770     open: false,
6771     
6772     buttonView : false,
6773     
6774     buttonWeight : 'default',
6775     
6776     buttonSize : 'md',
6777     
6778     showArrow : true,
6779     
6780     getAutoCreate : function(){
6781         
6782         
6783         var a = {
6784                 tag: 'a',
6785                 href : this.href || '#',
6786                 cls: '',
6787                 html : '',
6788                 cn : []
6789         };
6790         
6791         if(this.buttonView){
6792             a = {
6793                 tag: 'button',
6794                 href : this.href || '#',
6795                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6796                 html : this.html,
6797                 cn : []
6798             };
6799         }
6800         
6801         var cfg = {
6802             tag: 'li',
6803             cls: '',
6804             cn: [ a ]
6805         };
6806         
6807         if (this.active) {
6808             cfg.cls += ' active';
6809         }
6810         
6811         if (this.disabled) {
6812             cfg.cls += ' disabled';
6813         }
6814         if (this.open) {
6815             cfg.cls += ' open x-open';
6816         }
6817         // left icon..
6818         if (this.glyphicon || this.icon) {
6819             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6820             a.cn.push({ tag : 'i', cls : c }) ;
6821         }
6822         
6823         if(!this.buttonView){
6824             var span = {
6825                 tag: 'span',
6826                 html : this.html || ''
6827             };
6828
6829             a.cn.push(span);
6830             
6831         }
6832         
6833         if (this.badge !== '') {
6834             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6835         }
6836         
6837         if (this.menu) {
6838             
6839             if(this.showArrow){
6840                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6841             }
6842             
6843             a.cls += ' dropdown-toggle treeview' ;
6844         }
6845         
6846         return cfg;
6847     },
6848     
6849     initEvents : function()
6850     { 
6851         if (typeof (this.menu) != 'undefined') {
6852             this.menu.parentType = this.xtype;
6853             this.menu.triggerEl = this.el;
6854             this.menu = this.addxtype(Roo.apply({}, this.menu));
6855         }
6856         
6857         this.el.on('click', this.onClick, this);
6858         
6859         if(this.badge !== ''){
6860             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861         }
6862         
6863     },
6864     
6865     onClick : function(e)
6866     {
6867         if(this.disabled){
6868             e.preventDefault();
6869             return;
6870         }
6871         
6872         if(this.preventDefault){
6873             e.preventDefault();
6874         }
6875         
6876         this.fireEvent('click', this, e);
6877     },
6878     
6879     disable : function()
6880     {
6881         this.setDisabled(true);
6882     },
6883     
6884     enable : function()
6885     {
6886         this.setDisabled(false);
6887     },
6888     
6889     setDisabled : function(state)
6890     {
6891         if(this.disabled == state){
6892             return;
6893         }
6894         
6895         this.disabled = state;
6896         
6897         if (state) {
6898             this.el.addClass('disabled');
6899             return;
6900         }
6901         
6902         this.el.removeClass('disabled');
6903         
6904         return;
6905     },
6906     
6907     setActive : function(state)
6908     {
6909         if(this.active == state){
6910             return;
6911         }
6912         
6913         this.active = state;
6914         
6915         if (state) {
6916             this.el.addClass('active');
6917             return;
6918         }
6919         
6920         this.el.removeClass('active');
6921         
6922         return;
6923     },
6924     
6925     isActive: function () 
6926     {
6927         return this.active;
6928     },
6929     
6930     setBadge : function(str)
6931     {
6932         if(!this.badgeEl){
6933             return;
6934         }
6935         
6936         this.badgeEl.dom.innerHTML = str;
6937     }
6938     
6939    
6940      
6941  
6942 });
6943  
6944
6945  /*
6946  * - LGPL
6947  *
6948  *  Breadcrumb Nav
6949  * 
6950  */
6951 Roo.namespace('Roo.bootstrap.breadcrumb');
6952
6953
6954 /**
6955  * @class Roo.bootstrap.breadcrumb.Nav
6956  * @extends Roo.bootstrap.Component
6957  * Bootstrap Breadcrumb Nav Class
6958  *  
6959  * @children Roo.bootstrap.breadcrumb.Item
6960  * 
6961  * @constructor
6962  * Create a new breadcrumb.Nav
6963  * @param {Object} config The config object
6964  */
6965
6966
6967 Roo.bootstrap.breadcrumb.Nav = function(config){
6968     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969     
6970     
6971 };
6972
6973 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6974     
6975     getAutoCreate : function()
6976     {
6977
6978         var cfg = {
6979             tag: 'nav',
6980             cn : [
6981                 {
6982                     tag : 'ol',
6983                     cls : 'breadcrumb'
6984                 }
6985             ]
6986             
6987         };
6988           
6989         return cfg;
6990     },
6991     
6992     initEvents: function()
6993     {
6994         this.olEl = this.el.select('ol',true).first();    
6995     },
6996     getChildContainer : function()
6997     {
6998         return this.olEl;  
6999     }
7000     
7001 });
7002
7003  /*
7004  * - LGPL
7005  *
7006  *  Breadcrumb Item
7007  * 
7008  */
7009
7010
7011 /**
7012  * @class Roo.bootstrap.breadcrumb.Nav
7013  * @extends Roo.bootstrap.Component
7014  * Bootstrap Breadcrumb Nav Class
7015  *  
7016  * @children Roo.bootstrap.breadcrumb.Component
7017  * @cfg {String} html the content of the link.
7018  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7019  * @cfg {Boolean} active is it active
7020
7021  * 
7022  * @constructor
7023  * Create a new breadcrumb.Nav
7024  * @param {Object} config The config object
7025  */
7026
7027 Roo.bootstrap.breadcrumb.Item = function(config){
7028     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029     this.addEvents({
7030         // img events
7031         /**
7032          * @event click
7033          * The img click event for the img.
7034          * @param {Roo.EventObject} e
7035          */
7036         "click" : true
7037     });
7038     
7039 };
7040
7041 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7042     
7043     href: false,
7044     html : '',
7045     
7046     getAutoCreate : function()
7047     {
7048
7049         var cfg = {
7050             tag: 'li',
7051             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7052         };
7053         if (this.href !== false) {
7054             cfg.cn = [{
7055                 tag : 'a',
7056                 href : this.href,
7057                 html : this.html
7058             }];
7059         } else {
7060             cfg.html = this.html;
7061         }
7062         
7063         return cfg;
7064     },
7065     
7066     initEvents: function()
7067     {
7068         if (this.href) {
7069             this.el.select('a', true).first().on('click',this.onClick, this)
7070         }
7071         
7072     },
7073     onClick : function(e)
7074     {
7075         e.preventDefault();
7076         this.fireEvent('click',this,  e);
7077     }
7078     
7079 });
7080
7081  /*
7082  * - LGPL
7083  *
7084  * row
7085  * 
7086  */
7087
7088 /**
7089  * @class Roo.bootstrap.Row
7090  * @extends Roo.bootstrap.Component
7091  * Bootstrap Row class (contains columns...)
7092  * 
7093  * @constructor
7094  * Create a new Row
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Row = function(config){
7099     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7103     
7104     getAutoCreate : function(){
7105        return {
7106             cls: 'row clearfix'
7107        };
7108     }
7109     
7110     
7111 });
7112
7113  
7114
7115  /*
7116  * - LGPL
7117  *
7118  * pagination
7119  * 
7120  */
7121
7122 /**
7123  * @class Roo.bootstrap.Pagination
7124  * @extends Roo.bootstrap.Component
7125  * Bootstrap Pagination class
7126  * @cfg {String} size xs | sm | md | lg
7127  * @cfg {Boolean} inverse false | true
7128  * 
7129  * @constructor
7130  * Create a new Pagination
7131  * @param {Object} config The config object
7132  */
7133
7134 Roo.bootstrap.Pagination = function(config){
7135     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7136 };
7137
7138 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7139     
7140     cls: false,
7141     size: false,
7142     inverse: false,
7143     
7144     getAutoCreate : function(){
7145         var cfg = {
7146             tag: 'ul',
7147                 cls: 'pagination'
7148         };
7149         if (this.inverse) {
7150             cfg.cls += ' inverse';
7151         }
7152         if (this.html) {
7153             cfg.html=this.html;
7154         }
7155         if (this.cls) {
7156             cfg.cls += " " + this.cls;
7157         }
7158         return cfg;
7159     }
7160    
7161 });
7162
7163  
7164
7165  /*
7166  * - LGPL
7167  *
7168  * Pagination item
7169  * 
7170  */
7171
7172
7173 /**
7174  * @class Roo.bootstrap.PaginationItem
7175  * @extends Roo.bootstrap.Component
7176  * Bootstrap PaginationItem class
7177  * @cfg {String} html text
7178  * @cfg {String} href the link
7179  * @cfg {Boolean} preventDefault (true | false) default true
7180  * @cfg {Boolean} active (true | false) default false
7181  * @cfg {Boolean} disabled default false
7182  * 
7183  * 
7184  * @constructor
7185  * Create a new PaginationItem
7186  * @param {Object} config The config object
7187  */
7188
7189
7190 Roo.bootstrap.PaginationItem = function(config){
7191     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192     this.addEvents({
7193         // raw events
7194         /**
7195          * @event click
7196          * The raw click event for the entire grid.
7197          * @param {Roo.EventObject} e
7198          */
7199         "click" : true
7200     });
7201 };
7202
7203 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7204     
7205     href : false,
7206     html : false,
7207     preventDefault: true,
7208     active : false,
7209     cls : false,
7210     disabled: false,
7211     
7212     getAutoCreate : function(){
7213         var cfg= {
7214             tag: 'li',
7215             cn: [
7216                 {
7217                     tag : 'a',
7218                     href : this.href ? this.href : '#',
7219                     html : this.html ? this.html : ''
7220                 }
7221             ]
7222         };
7223         
7224         if(this.cls){
7225             cfg.cls = this.cls;
7226         }
7227         
7228         if(this.disabled){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7230         }
7231         
7232         if(this.active){
7233             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7234         }
7235         
7236         return cfg;
7237     },
7238     
7239     initEvents: function() {
7240         
7241         this.el.on('click', this.onClick, this);
7242         
7243     },
7244     onClick : function(e)
7245     {
7246         Roo.log('PaginationItem on click ');
7247         if(this.preventDefault){
7248             e.preventDefault();
7249         }
7250         
7251         if(this.disabled){
7252             return;
7253         }
7254         
7255         this.fireEvent('click', this, e);
7256     }
7257    
7258 });
7259
7260  
7261
7262  /*
7263  * - LGPL
7264  *
7265  * slider
7266  * 
7267  */
7268
7269
7270 /**
7271  * @class Roo.bootstrap.Slider
7272  * @extends Roo.bootstrap.Component
7273  * Bootstrap Slider class
7274  *    
7275  * @constructor
7276  * Create a new Slider
7277  * @param {Object} config The config object
7278  */
7279
7280 Roo.bootstrap.Slider = function(config){
7281     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7282 };
7283
7284 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7285     
7286     getAutoCreate : function(){
7287         
7288         var cfg = {
7289             tag: 'div',
7290             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7291             cn: [
7292                 {
7293                     tag: 'a',
7294                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7295                 }
7296             ]
7297         };
7298         
7299         return cfg;
7300     }
7301    
7302 });
7303
7304  /*
7305  * Based on:
7306  * Ext JS Library 1.1.1
7307  * Copyright(c) 2006-2007, Ext JS, LLC.
7308  *
7309  * Originally Released Under LGPL - original licence link has changed is not relivant.
7310  *
7311  * Fork - LGPL
7312  * <script type="text/javascript">
7313  */
7314  /**
7315  * @extends Roo.dd.DDProxy
7316  * @class Roo.grid.SplitDragZone
7317  * Support for Column Header resizing
7318  * @constructor
7319  * @param {Object} config
7320  */
7321 // private
7322 // This is a support class used internally by the Grid components
7323 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7324     this.grid = grid;
7325     this.view = grid.getView();
7326     this.proxy = this.view.resizeProxy;
7327     Roo.grid.SplitDragZone.superclass.constructor.call(
7328         this,
7329         hd, // ID
7330         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7331         {  // CONFIG
7332             dragElId : Roo.id(this.proxy.dom),
7333             resizeFrame:false
7334         }
7335     );
7336     
7337     this.setHandleElId(Roo.id(hd));
7338     if (hd2 !== false) {
7339         this.setOuterHandleElId(Roo.id(hd2));
7340     }
7341     
7342     this.scroll = false;
7343 };
7344 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7345     fly: Roo.Element.fly,
7346
7347     b4StartDrag : function(x, y){
7348         this.view.headersDisabled = true;
7349         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7350                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7351         );
7352         this.proxy.setHeight(h);
7353         
7354         // for old system colWidth really stored the actual width?
7355         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7356         // which in reality did not work.. - it worked only for fixed sizes
7357         // for resizable we need to use actual sizes.
7358         var w = this.cm.getColumnWidth(this.cellIndex);
7359         if (!this.view.mainWrap) {
7360             // bootstrap.
7361             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7362         }
7363         
7364         
7365         
7366         // this was w-this.grid.minColumnWidth;
7367         // doesnt really make sense? - w = thie curren width or the rendered one?
7368         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7369         this.resetConstraints();
7370         this.setXConstraint(minw, 1000);
7371         this.setYConstraint(0, 0);
7372         this.minX = x - minw;
7373         this.maxX = x + 1000;
7374         this.startPos = x;
7375         if (!this.view.mainWrap) { // this is Bootstrap code..
7376             this.getDragEl().style.display='block';
7377         }
7378         
7379         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7380     },
7381
7382
7383     handleMouseDown : function(e){
7384         ev = Roo.EventObject.setEvent(e);
7385         var t = this.fly(ev.getTarget());
7386         if(t.hasClass("x-grid-split")){
7387             this.cellIndex = this.view.getCellIndex(t.dom);
7388             this.split = t.dom;
7389             this.cm = this.grid.colModel;
7390             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7391                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7392             }
7393         }
7394     },
7395
7396     endDrag : function(e){
7397         this.view.headersDisabled = false;
7398         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7399         var diff = endX - this.startPos;
7400         // 
7401         var w = this.cm.getColumnWidth(this.cellIndex);
7402         if (!this.view.mainWrap) {
7403             w = 0;
7404         }
7405         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7406     },
7407
7408     autoOffset : function(){
7409         this.setDelta(0,0);
7410     }
7411 });/*
7412  * Based on:
7413  * Ext JS Library 1.1.1
7414  * Copyright(c) 2006-2007, Ext JS, LLC.
7415  *
7416  * Originally Released Under LGPL - original licence link has changed is not relivant.
7417  *
7418  * Fork - LGPL
7419  * <script type="text/javascript">
7420  */
7421
7422 /**
7423  * @class Roo.grid.AbstractSelectionModel
7424  * @extends Roo.util.Observable
7425  * @abstract
7426  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7427  * implemented by descendant classes.  This class should not be directly instantiated.
7428  * @constructor
7429  */
7430 Roo.grid.AbstractSelectionModel = function(){
7431     this.locked = false;
7432     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7433 };
7434
7435 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7436     /** @ignore Called by the grid automatically. Do not call directly. */
7437     init : function(grid){
7438         this.grid = grid;
7439         this.initEvents();
7440     },
7441
7442     /**
7443      * Locks the selections.
7444      */
7445     lock : function(){
7446         this.locked = true;
7447     },
7448
7449     /**
7450      * Unlocks the selections.
7451      */
7452     unlock : function(){
7453         this.locked = false;
7454     },
7455
7456     /**
7457      * Returns true if the selections are locked.
7458      * @return {Boolean}
7459      */
7460     isLocked : function(){
7461         return this.locked;
7462     }
7463 });/*
7464  * Based on:
7465  * Ext JS Library 1.1.1
7466  * Copyright(c) 2006-2007, Ext JS, LLC.
7467  *
7468  * Originally Released Under LGPL - original licence link has changed is not relivant.
7469  *
7470  * Fork - LGPL
7471  * <script type="text/javascript">
7472  */
7473 /**
7474  * @extends Roo.grid.AbstractSelectionModel
7475  * @class Roo.grid.RowSelectionModel
7476  * The default SelectionModel used by {@link Roo.grid.Grid}.
7477  * It supports multiple selections and keyboard selection/navigation. 
7478  * @constructor
7479  * @param {Object} config
7480  */
7481 Roo.grid.RowSelectionModel = function(config){
7482     Roo.apply(this, config);
7483     this.selections = new Roo.util.MixedCollection(false, function(o){
7484         return o.id;
7485     });
7486
7487     this.last = false;
7488     this.lastActive = false;
7489
7490     this.addEvents({
7491         /**
7492         * @event selectionchange
7493         * Fires when the selection changes
7494         * @param {SelectionModel} this
7495         */
7496        "selectionchange" : true,
7497        /**
7498         * @event afterselectionchange
7499         * Fires after the selection changes (eg. by key press or clicking)
7500         * @param {SelectionModel} this
7501         */
7502        "afterselectionchange" : true,
7503        /**
7504         * @event beforerowselect
7505         * Fires when a row is selected being selected, return false to cancel.
7506         * @param {SelectionModel} this
7507         * @param {Number} rowIndex The selected index
7508         * @param {Boolean} keepExisting False if other selections will be cleared
7509         */
7510        "beforerowselect" : true,
7511        /**
7512         * @event rowselect
7513         * Fires when a row is selected.
7514         * @param {SelectionModel} this
7515         * @param {Number} rowIndex The selected index
7516         * @param {Roo.data.Record} r The record
7517         */
7518        "rowselect" : true,
7519        /**
7520         * @event rowdeselect
7521         * Fires when a row is deselected.
7522         * @param {SelectionModel} this
7523         * @param {Number} rowIndex The selected index
7524         */
7525         "rowdeselect" : true
7526     });
7527     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7528     this.locked = false;
7529 };
7530
7531 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7532     /**
7533      * @cfg {Boolean} singleSelect
7534      * True to allow selection of only one row at a time (defaults to false)
7535      */
7536     singleSelect : false,
7537
7538     // private
7539     initEvents : function(){
7540
7541         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7542             this.grid.on("mousedown", this.handleMouseDown, this);
7543         }else{ // allow click to work like normal
7544             this.grid.on("rowclick", this.handleDragableRowClick, this);
7545         }
7546         // bootstrap does not have a view..
7547         var view = this.grid.view ? this.grid.view : this.grid;
7548         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7549             "up" : function(e){
7550                 if(!e.shiftKey){
7551                     this.selectPrevious(e.shiftKey);
7552                 }else if(this.last !== false && this.lastActive !== false){
7553                     var last = this.last;
7554                     this.selectRange(this.last,  this.lastActive-1);
7555                     view.focusRow(this.lastActive);
7556                     if(last !== false){
7557                         this.last = last;
7558                     }
7559                 }else{
7560                     this.selectFirstRow();
7561                 }
7562                 this.fireEvent("afterselectionchange", this);
7563             },
7564             "down" : function(e){
7565                 if(!e.shiftKey){
7566                     this.selectNext(e.shiftKey);
7567                 }else if(this.last !== false && this.lastActive !== false){
7568                     var last = this.last;
7569                     this.selectRange(this.last,  this.lastActive+1);
7570                     view.focusRow(this.lastActive);
7571                     if(last !== false){
7572                         this.last = last;
7573                     }
7574                 }else{
7575                     this.selectFirstRow();
7576                 }
7577                 this.fireEvent("afterselectionchange", this);
7578             },
7579             scope: this
7580         });
7581
7582          
7583         view.on("refresh", this.onRefresh, this);
7584         view.on("rowupdated", this.onRowUpdated, this);
7585         view.on("rowremoved", this.onRemove, this);
7586     },
7587
7588     // private
7589     onRefresh : function(){
7590         var ds = this.grid.ds, i, v = this.grid.view;
7591         var s = this.selections;
7592         s.each(function(r){
7593             if((i = ds.indexOfId(r.id)) != -1){
7594                 v.onRowSelect(i);
7595                 s.add(ds.getAt(i)); // updating the selection relate data
7596             }else{
7597                 s.remove(r);
7598             }
7599         });
7600     },
7601
7602     // private
7603     onRemove : function(v, index, r){
7604         this.selections.remove(r);
7605     },
7606
7607     // private
7608     onRowUpdated : function(v, index, r){
7609         if(this.isSelected(r)){
7610             v.onRowSelect(index);
7611         }
7612     },
7613
7614     /**
7615      * Select records.
7616      * @param {Array} records The records to select
7617      * @param {Boolean} keepExisting (optional) True to keep existing selections
7618      */
7619     selectRecords : function(records, keepExisting){
7620         if(!keepExisting){
7621             this.clearSelections();
7622         }
7623         var ds = this.grid.ds;
7624         for(var i = 0, len = records.length; i < len; i++){
7625             this.selectRow(ds.indexOf(records[i]), true);
7626         }
7627     },
7628
7629     /**
7630      * Gets the number of selected rows.
7631      * @return {Number}
7632      */
7633     getCount : function(){
7634         return this.selections.length;
7635     },
7636
7637     /**
7638      * Selects the first row in the grid.
7639      */
7640     selectFirstRow : function(){
7641         this.selectRow(0);
7642     },
7643
7644     /**
7645      * Select the last row.
7646      * @param {Boolean} keepExisting (optional) True to keep existing selections
7647      */
7648     selectLastRow : function(keepExisting){
7649         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7650     },
7651
7652     /**
7653      * Selects the row immediately following the last selected row.
7654      * @param {Boolean} keepExisting (optional) True to keep existing selections
7655      */
7656     selectNext : function(keepExisting){
7657         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7658             this.selectRow(this.last+1, keepExisting);
7659             var view = this.grid.view ? this.grid.view : this.grid;
7660             view.focusRow(this.last);
7661         }
7662     },
7663
7664     /**
7665      * Selects the row that precedes the last selected row.
7666      * @param {Boolean} keepExisting (optional) True to keep existing selections
7667      */
7668     selectPrevious : function(keepExisting){
7669         if(this.last){
7670             this.selectRow(this.last-1, keepExisting);
7671             var view = this.grid.view ? this.grid.view : this.grid;
7672             view.focusRow(this.last);
7673         }
7674     },
7675
7676     /**
7677      * Returns the selected records
7678      * @return {Array} Array of selected records
7679      */
7680     getSelections : function(){
7681         return [].concat(this.selections.items);
7682     },
7683
7684     /**
7685      * Returns the first selected record.
7686      * @return {Record}
7687      */
7688     getSelected : function(){
7689         return this.selections.itemAt(0);
7690     },
7691
7692
7693     /**
7694      * Clears all selections.
7695      */
7696     clearSelections : function(fast){
7697         if(this.locked) {
7698             return;
7699         }
7700         if(fast !== true){
7701             var ds = this.grid.ds;
7702             var s = this.selections;
7703             s.each(function(r){
7704                 this.deselectRow(ds.indexOfId(r.id));
7705             }, this);
7706             s.clear();
7707         }else{
7708             this.selections.clear();
7709         }
7710         this.last = false;
7711     },
7712
7713
7714     /**
7715      * Selects all rows.
7716      */
7717     selectAll : function(){
7718         if(this.locked) {
7719             return;
7720         }
7721         this.selections.clear();
7722         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7723             this.selectRow(i, true);
7724         }
7725     },
7726
7727     /**
7728      * Returns True if there is a selection.
7729      * @return {Boolean}
7730      */
7731     hasSelection : function(){
7732         return this.selections.length > 0;
7733     },
7734
7735     /**
7736      * Returns True if the specified row is selected.
7737      * @param {Number/Record} record The record or index of the record to check
7738      * @return {Boolean}
7739      */
7740     isSelected : function(index){
7741         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7742         return (r && this.selections.key(r.id) ? true : false);
7743     },
7744
7745     /**
7746      * Returns True if the specified record id is selected.
7747      * @param {String} id The id of record to check
7748      * @return {Boolean}
7749      */
7750     isIdSelected : function(id){
7751         return (this.selections.key(id) ? true : false);
7752     },
7753
7754     // private
7755     handleMouseDown : function(e, t)
7756     {
7757         var view = this.grid.view ? this.grid.view : this.grid;
7758         var rowIndex;
7759         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7760             return;
7761         };
7762         if(e.shiftKey && this.last !== false){
7763             var last = this.last;
7764             this.selectRange(last, rowIndex, e.ctrlKey);
7765             this.last = last; // reset the last
7766             view.focusRow(rowIndex);
7767         }else{
7768             var isSelected = this.isSelected(rowIndex);
7769             if(e.button !== 0 && isSelected){
7770                 view.focusRow(rowIndex);
7771             }else if(e.ctrlKey && isSelected){
7772                 this.deselectRow(rowIndex);
7773             }else if(!isSelected){
7774                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7775                 view.focusRow(rowIndex);
7776             }
7777         }
7778         this.fireEvent("afterselectionchange", this);
7779     },
7780     // private
7781     handleDragableRowClick :  function(grid, rowIndex, e) 
7782     {
7783         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7784             this.selectRow(rowIndex, false);
7785             var view = this.grid.view ? this.grid.view : this.grid;
7786             view.focusRow(rowIndex);
7787              this.fireEvent("afterselectionchange", this);
7788         }
7789     },
7790     
7791     /**
7792      * Selects multiple rows.
7793      * @param {Array} rows Array of the indexes of the row to select
7794      * @param {Boolean} keepExisting (optional) True to keep existing selections
7795      */
7796     selectRows : function(rows, keepExisting){
7797         if(!keepExisting){
7798             this.clearSelections();
7799         }
7800         for(var i = 0, len = rows.length; i < len; i++){
7801             this.selectRow(rows[i], true);
7802         }
7803     },
7804
7805     /**
7806      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7807      * @param {Number} startRow The index of the first row in the range
7808      * @param {Number} endRow The index of the last row in the range
7809      * @param {Boolean} keepExisting (optional) True to retain existing selections
7810      */
7811     selectRange : function(startRow, endRow, keepExisting){
7812         if(this.locked) {
7813             return;
7814         }
7815         if(!keepExisting){
7816             this.clearSelections();
7817         }
7818         if(startRow <= endRow){
7819             for(var i = startRow; i <= endRow; i++){
7820                 this.selectRow(i, true);
7821             }
7822         }else{
7823             for(var i = startRow; i >= endRow; i--){
7824                 this.selectRow(i, true);
7825             }
7826         }
7827     },
7828
7829     /**
7830      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7831      * @param {Number} startRow The index of the first row in the range
7832      * @param {Number} endRow The index of the last row in the range
7833      */
7834     deselectRange : function(startRow, endRow, preventViewNotify){
7835         if(this.locked) {
7836             return;
7837         }
7838         for(var i = startRow; i <= endRow; i++){
7839             this.deselectRow(i, preventViewNotify);
7840         }
7841     },
7842
7843     /**
7844      * Selects a row.
7845      * @param {Number} row The index of the row to select
7846      * @param {Boolean} keepExisting (optional) True to keep existing selections
7847      */
7848     selectRow : function(index, keepExisting, preventViewNotify){
7849         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7850             return;
7851         }
7852         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7853             if(!keepExisting || this.singleSelect){
7854                 this.clearSelections();
7855             }
7856             var r = this.grid.ds.getAt(index);
7857             this.selections.add(r);
7858             this.last = this.lastActive = index;
7859             if(!preventViewNotify){
7860                 var view = this.grid.view ? this.grid.view : this.grid;
7861                 view.onRowSelect(index);
7862             }
7863             this.fireEvent("rowselect", this, index, r);
7864             this.fireEvent("selectionchange", this);
7865         }
7866     },
7867
7868     /**
7869      * Deselects a row.
7870      * @param {Number} row The index of the row to deselect
7871      */
7872     deselectRow : function(index, preventViewNotify){
7873         if(this.locked) {
7874             return;
7875         }
7876         if(this.last == index){
7877             this.last = false;
7878         }
7879         if(this.lastActive == index){
7880             this.lastActive = false;
7881         }
7882         var r = this.grid.ds.getAt(index);
7883         this.selections.remove(r);
7884         if(!preventViewNotify){
7885             var view = this.grid.view ? this.grid.view : this.grid;
7886             view.onRowDeselect(index);
7887         }
7888         this.fireEvent("rowdeselect", this, index);
7889         this.fireEvent("selectionchange", this);
7890     },
7891
7892     // private
7893     restoreLast : function(){
7894         if(this._last){
7895             this.last = this._last;
7896         }
7897     },
7898
7899     // private
7900     acceptsNav : function(row, col, cm){
7901         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7902     },
7903
7904     // private
7905     onEditorKey : function(field, e){
7906         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7907         if(k == e.TAB){
7908             e.stopEvent();
7909             ed.completeEdit();
7910             if(e.shiftKey){
7911                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7912             }else{
7913                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7914             }
7915         }else if(k == e.ENTER && !e.ctrlKey){
7916             e.stopEvent();
7917             ed.completeEdit();
7918             if(e.shiftKey){
7919                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7920             }else{
7921                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7922             }
7923         }else if(k == e.ESC){
7924             ed.cancelEdit();
7925         }
7926         if(newCell){
7927             g.startEditing(newCell[0], newCell[1]);
7928         }
7929     }
7930 });/*
7931  * Based on:
7932  * Ext JS Library 1.1.1
7933  * Copyright(c) 2006-2007, Ext JS, LLC.
7934  *
7935  * Originally Released Under LGPL - original licence link has changed is not relivant.
7936  *
7937  * Fork - LGPL
7938  * <script type="text/javascript">
7939  */
7940  
7941
7942 /**
7943  * @class Roo.grid.ColumnModel
7944  * @extends Roo.util.Observable
7945  * This is the default implementation of a ColumnModel used by the Grid. It defines
7946  * the columns in the grid.
7947  * <br>Usage:<br>
7948  <pre><code>
7949  var colModel = new Roo.grid.ColumnModel([
7950         {header: "Ticker", width: 60, sortable: true, locked: true},
7951         {header: "Company Name", width: 150, sortable: true},
7952         {header: "Market Cap.", width: 100, sortable: true},
7953         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7954         {header: "Employees", width: 100, sortable: true, resizable: false}
7955  ]);
7956  </code></pre>
7957  * <p>
7958  
7959  * The config options listed for this class are options which may appear in each
7960  * individual column definition.
7961  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7962  * @constructor
7963  * @param {Object} config An Array of column config objects. See this class's
7964  * config objects for details.
7965 */
7966 Roo.grid.ColumnModel = function(config){
7967         /**
7968      * The config passed into the constructor
7969      */
7970     this.config = []; //config;
7971     this.lookup = {};
7972
7973     // if no id, create one
7974     // if the column does not have a dataIndex mapping,
7975     // map it to the order it is in the config
7976     for(var i = 0, len = config.length; i < len; i++){
7977         this.addColumn(config[i]);
7978         
7979     }
7980
7981     /**
7982      * The width of columns which have no width specified (defaults to 100)
7983      * @type Number
7984      */
7985     this.defaultWidth = 100;
7986
7987     /**
7988      * Default sortable of columns which have no sortable specified (defaults to false)
7989      * @type Boolean
7990      */
7991     this.defaultSortable = false;
7992
7993     this.addEvents({
7994         /**
7995              * @event widthchange
7996              * Fires when the width of a column changes.
7997              * @param {ColumnModel} this
7998              * @param {Number} columnIndex The column index
7999              * @param {Number} newWidth The new width
8000              */
8001             "widthchange": true,
8002         /**
8003              * @event headerchange
8004              * Fires when the text of a header changes.
8005              * @param {ColumnModel} this
8006              * @param {Number} columnIndex The column index
8007              * @param {Number} newText The new header text
8008              */
8009             "headerchange": true,
8010         /**
8011              * @event hiddenchange
8012              * Fires when a column is hidden or "unhidden".
8013              * @param {ColumnModel} this
8014              * @param {Number} columnIndex The column index
8015              * @param {Boolean} hidden true if hidden, false otherwise
8016              */
8017             "hiddenchange": true,
8018             /**
8019          * @event columnmoved
8020          * Fires when a column is moved.
8021          * @param {ColumnModel} this
8022          * @param {Number} oldIndex
8023          * @param {Number} newIndex
8024          */
8025         "columnmoved" : true,
8026         /**
8027          * @event columlockchange
8028          * Fires when a column's locked state is changed
8029          * @param {ColumnModel} this
8030          * @param {Number} colIndex
8031          * @param {Boolean} locked true if locked
8032          */
8033         "columnlockchange" : true
8034     });
8035     Roo.grid.ColumnModel.superclass.constructor.call(this);
8036 };
8037 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8038     /**
8039      * @cfg {String} header The header text to display in the Grid view.
8040      */
8041         /**
8042      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8043      */
8044         /**
8045      * @cfg {String} smHeader Header at Bootsrap Small width
8046      */
8047         /**
8048      * @cfg {String} mdHeader Header at Bootsrap Medium width
8049      */
8050         /**
8051      * @cfg {String} lgHeader Header at Bootsrap Large width
8052      */
8053         /**
8054      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8055      */
8056     /**
8057      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8058      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8059      * specified, the column's index is used as an index into the Record's data Array.
8060      */
8061     /**
8062      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8063      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8064      */
8065     /**
8066      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8067      * Defaults to the value of the {@link #defaultSortable} property.
8068      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8069      */
8070     /**
8071      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8072      */
8073     /**
8074      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8075      */
8076     /**
8077      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8078      */
8079     /**
8080      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8081      */
8082     /**
8083      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8084      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8085      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8086      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8087      */
8088        /**
8089      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8090      */
8091     /**
8092      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8093      */
8094     /**
8095      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8096      */
8097     /**
8098      * @cfg {String} cursor (Optional)
8099      */
8100     /**
8101      * @cfg {String} tooltip (Optional)
8102      */
8103     /**
8104      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8105      */
8106     /**
8107      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8108      */
8109     /**
8110      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8111      */
8112     /**
8113      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8114      */
8115         /**
8116      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8117      */
8118     /**
8119      * Returns the id of the column at the specified index.
8120      * @param {Number} index The column index
8121      * @return {String} the id
8122      */
8123     getColumnId : function(index){
8124         return this.config[index].id;
8125     },
8126
8127     /**
8128      * Returns the column for a specified id.
8129      * @param {String} id The column id
8130      * @return {Object} the column
8131      */
8132     getColumnById : function(id){
8133         return this.lookup[id];
8134     },
8135
8136     
8137     /**
8138      * Returns the column Object for a specified dataIndex.
8139      * @param {String} dataIndex The column dataIndex
8140      * @return {Object|Boolean} the column or false if not found
8141      */
8142     getColumnByDataIndex: function(dataIndex){
8143         var index = this.findColumnIndex(dataIndex);
8144         return index > -1 ? this.config[index] : false;
8145     },
8146     
8147     /**
8148      * Returns the index for a specified column id.
8149      * @param {String} id The column id
8150      * @return {Number} the index, or -1 if not found
8151      */
8152     getIndexById : function(id){
8153         for(var i = 0, len = this.config.length; i < len; i++){
8154             if(this.config[i].id == id){
8155                 return i;
8156             }
8157         }
8158         return -1;
8159     },
8160     
8161     /**
8162      * Returns the index for a specified column dataIndex.
8163      * @param {String} dataIndex The column dataIndex
8164      * @return {Number} the index, or -1 if not found
8165      */
8166     
8167     findColumnIndex : function(dataIndex){
8168         for(var i = 0, len = this.config.length; i < len; i++){
8169             if(this.config[i].dataIndex == dataIndex){
8170                 return i;
8171             }
8172         }
8173         return -1;
8174     },
8175     
8176     
8177     moveColumn : function(oldIndex, newIndex){
8178         var c = this.config[oldIndex];
8179         this.config.splice(oldIndex, 1);
8180         this.config.splice(newIndex, 0, c);
8181         this.dataMap = null;
8182         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8183     },
8184
8185     isLocked : function(colIndex){
8186         return this.config[colIndex].locked === true;
8187     },
8188
8189     setLocked : function(colIndex, value, suppressEvent){
8190         if(this.isLocked(colIndex) == value){
8191             return;
8192         }
8193         this.config[colIndex].locked = value;
8194         if(!suppressEvent){
8195             this.fireEvent("columnlockchange", this, colIndex, value);
8196         }
8197     },
8198
8199     getTotalLockedWidth : function(){
8200         var totalWidth = 0;
8201         for(var i = 0; i < this.config.length; i++){
8202             if(this.isLocked(i) && !this.isHidden(i)){
8203                 this.totalWidth += this.getColumnWidth(i);
8204             }
8205         }
8206         return totalWidth;
8207     },
8208
8209     getLockedCount : function(){
8210         for(var i = 0, len = this.config.length; i < len; i++){
8211             if(!this.isLocked(i)){
8212                 return i;
8213             }
8214         }
8215         
8216         return this.config.length;
8217     },
8218
8219     /**
8220      * Returns the number of columns.
8221      * @return {Number}
8222      */
8223     getColumnCount : function(visibleOnly){
8224         if(visibleOnly === true){
8225             var c = 0;
8226             for(var i = 0, len = this.config.length; i < len; i++){
8227                 if(!this.isHidden(i)){
8228                     c++;
8229                 }
8230             }
8231             return c;
8232         }
8233         return this.config.length;
8234     },
8235
8236     /**
8237      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8238      * @param {Function} fn
8239      * @param {Object} scope (optional)
8240      * @return {Array} result
8241      */
8242     getColumnsBy : function(fn, scope){
8243         var r = [];
8244         for(var i = 0, len = this.config.length; i < len; i++){
8245             var c = this.config[i];
8246             if(fn.call(scope||this, c, i) === true){
8247                 r[r.length] = c;
8248             }
8249         }
8250         return r;
8251     },
8252
8253     /**
8254      * Returns true if the specified column is sortable.
8255      * @param {Number} col The column index
8256      * @return {Boolean}
8257      */
8258     isSortable : function(col){
8259         if(typeof this.config[col].sortable == "undefined"){
8260             return this.defaultSortable;
8261         }
8262         return this.config[col].sortable;
8263     },
8264
8265     /**
8266      * Returns the rendering (formatting) function defined for the column.
8267      * @param {Number} col The column index.
8268      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8269      */
8270     getRenderer : function(col){
8271         if(!this.config[col].renderer){
8272             return Roo.grid.ColumnModel.defaultRenderer;
8273         }
8274         return this.config[col].renderer;
8275     },
8276
8277     /**
8278      * Sets the rendering (formatting) function for a column.
8279      * @param {Number} col The column index
8280      * @param {Function} fn The function to use to process the cell's raw data
8281      * to return HTML markup for the grid view. The render function is called with
8282      * the following parameters:<ul>
8283      * <li>Data value.</li>
8284      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8285      * <li>css A CSS style string to apply to the table cell.</li>
8286      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8287      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8288      * <li>Row index</li>
8289      * <li>Column index</li>
8290      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8291      */
8292     setRenderer : function(col, fn){
8293         this.config[col].renderer = fn;
8294     },
8295
8296     /**
8297      * Returns the width for the specified column.
8298      * @param {Number} col The column index
8299      * @param (optional) {String} gridSize bootstrap width size.
8300      * @return {Number}
8301      */
8302     getColumnWidth : function(col, gridSize)
8303         {
8304                 var cfg = this.config[col];
8305                 
8306                 if (typeof(gridSize) == 'undefined') {
8307                         return cfg.width * 1 || this.defaultWidth;
8308                 }
8309                 if (gridSize === false) { // if we set it..
8310                         return cfg.width || false;
8311                 }
8312                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8313                 
8314                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8315                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8316                                 continue;
8317                         }
8318                         return cfg[ sizes[i] ];
8319                 }
8320                 return 1;
8321                 
8322     },
8323
8324     /**
8325      * Sets the width for a column.
8326      * @param {Number} col The column index
8327      * @param {Number} width The new width
8328      */
8329     setColumnWidth : function(col, width, suppressEvent){
8330         this.config[col].width = width;
8331         this.totalWidth = null;
8332         if(!suppressEvent){
8333              this.fireEvent("widthchange", this, col, width);
8334         }
8335     },
8336
8337     /**
8338      * Returns the total width of all columns.
8339      * @param {Boolean} includeHidden True to include hidden column widths
8340      * @return {Number}
8341      */
8342     getTotalWidth : function(includeHidden){
8343         if(!this.totalWidth){
8344             this.totalWidth = 0;
8345             for(var i = 0, len = this.config.length; i < len; i++){
8346                 if(includeHidden || !this.isHidden(i)){
8347                     this.totalWidth += this.getColumnWidth(i);
8348                 }
8349             }
8350         }
8351         return this.totalWidth;
8352     },
8353
8354     /**
8355      * Returns the header for the specified column.
8356      * @param {Number} col The column index
8357      * @return {String}
8358      */
8359     getColumnHeader : function(col){
8360         return this.config[col].header;
8361     },
8362
8363     /**
8364      * Sets the header for a column.
8365      * @param {Number} col The column index
8366      * @param {String} header The new header
8367      */
8368     setColumnHeader : function(col, header){
8369         this.config[col].header = header;
8370         this.fireEvent("headerchange", this, col, header);
8371     },
8372
8373     /**
8374      * Returns the tooltip for the specified column.
8375      * @param {Number} col The column index
8376      * @return {String}
8377      */
8378     getColumnTooltip : function(col){
8379             return this.config[col].tooltip;
8380     },
8381     /**
8382      * Sets the tooltip for a column.
8383      * @param {Number} col The column index
8384      * @param {String} tooltip The new tooltip
8385      */
8386     setColumnTooltip : function(col, tooltip){
8387             this.config[col].tooltip = tooltip;
8388     },
8389
8390     /**
8391      * Returns the dataIndex for the specified column.
8392      * @param {Number} col The column index
8393      * @return {Number}
8394      */
8395     getDataIndex : function(col){
8396         return this.config[col].dataIndex;
8397     },
8398
8399     /**
8400      * Sets the dataIndex for a column.
8401      * @param {Number} col The column index
8402      * @param {Number} dataIndex The new dataIndex
8403      */
8404     setDataIndex : function(col, dataIndex){
8405         this.config[col].dataIndex = dataIndex;
8406     },
8407
8408     
8409     
8410     /**
8411      * Returns true if the cell is editable.
8412      * @param {Number} colIndex The column index
8413      * @param {Number} rowIndex The row index - this is nto actually used..?
8414      * @return {Boolean}
8415      */
8416     isCellEditable : function(colIndex, rowIndex){
8417         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8418     },
8419
8420     /**
8421      * Returns the editor defined for the cell/column.
8422      * return false or null to disable editing.
8423      * @param {Number} colIndex The column index
8424      * @param {Number} rowIndex The row index
8425      * @return {Object}
8426      */
8427     getCellEditor : function(colIndex, rowIndex){
8428         return this.config[colIndex].editor;
8429     },
8430
8431     /**
8432      * Sets if a column is editable.
8433      * @param {Number} col The column index
8434      * @param {Boolean} editable True if the column is editable
8435      */
8436     setEditable : function(col, editable){
8437         this.config[col].editable = editable;
8438     },
8439
8440
8441     /**
8442      * Returns true if the column is hidden.
8443      * @param {Number} colIndex The column index
8444      * @return {Boolean}
8445      */
8446     isHidden : function(colIndex){
8447         return this.config[colIndex].hidden;
8448     },
8449
8450
8451     /**
8452      * Returns true if the column width cannot be changed
8453      */
8454     isFixed : function(colIndex){
8455         return this.config[colIndex].fixed;
8456     },
8457
8458     /**
8459      * Returns true if the column can be resized
8460      * @return {Boolean}
8461      */
8462     isResizable : function(colIndex){
8463         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8464     },
8465     /**
8466      * Sets if a column is hidden.
8467      * @param {Number} colIndex The column index
8468      * @param {Boolean} hidden True if the column is hidden
8469      */
8470     setHidden : function(colIndex, hidden){
8471         this.config[colIndex].hidden = hidden;
8472         this.totalWidth = null;
8473         this.fireEvent("hiddenchange", this, colIndex, hidden);
8474     },
8475
8476     /**
8477      * Sets the editor for a column.
8478      * @param {Number} col The column index
8479      * @param {Object} editor The editor object
8480      */
8481     setEditor : function(col, editor){
8482         this.config[col].editor = editor;
8483     },
8484     /**
8485      * Add a column (experimental...) - defaults to adding to the end..
8486      * @param {Object} config 
8487     */
8488     addColumn : function(c)
8489     {
8490     
8491         var i = this.config.length;
8492         this.config[i] = c;
8493         
8494         if(typeof c.dataIndex == "undefined"){
8495             c.dataIndex = i;
8496         }
8497         if(typeof c.renderer == "string"){
8498             c.renderer = Roo.util.Format[c.renderer];
8499         }
8500         if(typeof c.id == "undefined"){
8501             c.id = Roo.id();
8502         }
8503         if(c.editor && c.editor.xtype){
8504             c.editor  = Roo.factory(c.editor, Roo.grid);
8505         }
8506         if(c.editor && c.editor.isFormField){
8507             c.editor = new Roo.grid.GridEditor(c.editor);
8508         }
8509         this.lookup[c.id] = c;
8510     }
8511     
8512 });
8513
8514 Roo.grid.ColumnModel.defaultRenderer = function(value)
8515 {
8516     if(typeof value == "object") {
8517         return value;
8518     }
8519         if(typeof value == "string" && value.length < 1){
8520             return "&#160;";
8521         }
8522     
8523         return String.format("{0}", value);
8524 };
8525
8526 // Alias for backwards compatibility
8527 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8528 /*
8529  * Based on:
8530  * Ext JS Library 1.1.1
8531  * Copyright(c) 2006-2007, Ext JS, LLC.
8532  *
8533  * Originally Released Under LGPL - original licence link has changed is not relivant.
8534  *
8535  * Fork - LGPL
8536  * <script type="text/javascript">
8537  */
8538  
8539 /**
8540  * @class Roo.LoadMask
8541  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8542  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8543  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8544  * element's UpdateManager load indicator and will be destroyed after the initial load.
8545  * @constructor
8546  * Create a new LoadMask
8547  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8548  * @param {Object} config The config object
8549  */
8550 Roo.LoadMask = function(el, config){
8551     this.el = Roo.get(el);
8552     Roo.apply(this, config);
8553     if(this.store){
8554         this.store.on('beforeload', this.onBeforeLoad, this);
8555         this.store.on('load', this.onLoad, this);
8556         this.store.on('loadexception', this.onLoadException, this);
8557         this.removeMask = false;
8558     }else{
8559         var um = this.el.getUpdateManager();
8560         um.showLoadIndicator = false; // disable the default indicator
8561         um.on('beforeupdate', this.onBeforeLoad, this);
8562         um.on('update', this.onLoad, this);
8563         um.on('failure', this.onLoad, this);
8564         this.removeMask = true;
8565     }
8566 };
8567
8568 Roo.LoadMask.prototype = {
8569     /**
8570      * @cfg {Boolean} removeMask
8571      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8572      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8573      */
8574     removeMask : false,
8575     /**
8576      * @cfg {String} msg
8577      * The text to display in a centered loading message box (defaults to 'Loading...')
8578      */
8579     msg : 'Loading...',
8580     /**
8581      * @cfg {String} msgCls
8582      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8583      */
8584     msgCls : 'x-mask-loading',
8585
8586     /**
8587      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8588      * @type Boolean
8589      */
8590     disabled: false,
8591
8592     /**
8593      * Disables the mask to prevent it from being displayed
8594      */
8595     disable : function(){
8596        this.disabled = true;
8597     },
8598
8599     /**
8600      * Enables the mask so that it can be displayed
8601      */
8602     enable : function(){
8603         this.disabled = false;
8604     },
8605     
8606     onLoadException : function()
8607     {
8608         Roo.log(arguments);
8609         
8610         if (typeof(arguments[3]) != 'undefined') {
8611             Roo.MessageBox.alert("Error loading",arguments[3]);
8612         } 
8613         /*
8614         try {
8615             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8616                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8617             }   
8618         } catch(e) {
8619             
8620         }
8621         */
8622     
8623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8624     },
8625     // private
8626     onLoad : function()
8627     {
8628         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8629     },
8630
8631     // private
8632     onBeforeLoad : function(){
8633         if(!this.disabled){
8634             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8635         }
8636     },
8637
8638     // private
8639     destroy : function(){
8640         if(this.store){
8641             this.store.un('beforeload', this.onBeforeLoad, this);
8642             this.store.un('load', this.onLoad, this);
8643             this.store.un('loadexception', this.onLoadException, this);
8644         }else{
8645             var um = this.el.getUpdateManager();
8646             um.un('beforeupdate', this.onBeforeLoad, this);
8647             um.un('update', this.onLoad, this);
8648             um.un('failure', this.onLoad, this);
8649         }
8650     }
8651 };/**
8652  * @class Roo.bootstrap.Table
8653  * @licence LGBL
8654  * @extends Roo.bootstrap.Component
8655  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8656  * Similar to Roo.grid.Grid
8657  * <pre><code>
8658  var table = Roo.factory({
8659     xtype : 'Table',
8660     xns : Roo.bootstrap,
8661     autoSizeColumns: true,
8662     
8663     
8664     store : {
8665         xtype : 'Store',
8666         xns : Roo.data,
8667         remoteSort : true,
8668         sortInfo : { direction : 'ASC', field: 'name' },
8669         proxy : {
8670            xtype : 'HttpProxy',
8671            xns : Roo.data,
8672            method : 'GET',
8673            url : 'https://example.com/some.data.url.json'
8674         },
8675         reader : {
8676            xtype : 'JsonReader',
8677            xns : Roo.data,
8678            fields : [ 'id', 'name', whatever' ],
8679            id : 'id',
8680            root : 'data'
8681         }
8682     },
8683     cm : [
8684         {
8685             xtype : 'ColumnModel',
8686             xns : Roo.grid,
8687             align : 'center',
8688             cursor : 'pointer',
8689             dataIndex : 'is_in_group',
8690             header : "Name",
8691             sortable : true,
8692             renderer : function(v, x , r) {  
8693             
8694                 return String.format("{0}", v)
8695             }
8696             width : 3
8697         } // more columns..
8698     ],
8699     selModel : {
8700         xtype : 'RowSelectionModel',
8701         xns : Roo.bootstrap.Table
8702         // you can add listeners to catch selection change here....
8703     }
8704      
8705
8706  });
8707  // set any options
8708  grid.render(Roo.get("some-div"));
8709 </code></pre>
8710
8711 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8712
8713
8714
8715  *
8716  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8717  * @cfg {Roo.data.Store} store The data store to use
8718  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8719  * 
8720  * @cfg {String} cls table class
8721  *
8722  * 
8723  * @cfg {boolean} striped Should the rows be alternative striped
8724  * @cfg {boolean} bordered Add borders to the table
8725  * @cfg {boolean} hover Add hover highlighting
8726  * @cfg {boolean} condensed Format condensed
8727  * @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,
8728  *                also adds table-responsive (see bootstrap docs for details)
8729  * @cfg {Boolean} loadMask (true|false) default false
8730  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8731  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8732  * @cfg {Boolean} rowSelection (true|false) default false
8733  * @cfg {Boolean} cellSelection (true|false) default false
8734  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8735  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8736  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8737  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8738  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8739  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8740  * 
8741  * @constructor
8742  * Create a new Table
8743  * @param {Object} config The config object
8744  */
8745
8746 Roo.bootstrap.Table = function(config)
8747 {
8748     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8749      
8750     // BC...
8751     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8752     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8753     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8754     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8755     
8756     this.view = this; // compat with grid.
8757     
8758     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8759     if (this.sm) {
8760         this.sm.grid = this;
8761         this.selModel = Roo.factory(this.sm, Roo.grid);
8762         this.sm = this.selModel;
8763         this.sm.xmodule = this.xmodule || false;
8764     }
8765     
8766     if (this.cm && typeof(this.cm.config) == 'undefined') {
8767         this.colModel = new Roo.grid.ColumnModel(this.cm);
8768         this.cm = this.colModel;
8769         this.cm.xmodule = this.xmodule || false;
8770     }
8771     if (this.store) {
8772         this.store= Roo.factory(this.store, Roo.data);
8773         this.ds = this.store;
8774         this.ds.xmodule = this.xmodule || false;
8775          
8776     }
8777     if (this.footer && this.store) {
8778         this.footer.dataSource = this.ds;
8779         this.footer = Roo.factory(this.footer);
8780     }
8781     
8782     /** @private */
8783     this.addEvents({
8784         /**
8785          * @event cellclick
8786          * Fires when a cell is clicked
8787          * @param {Roo.bootstrap.Table} this
8788          * @param {Roo.Element} el
8789          * @param {Number} rowIndex
8790          * @param {Number} columnIndex
8791          * @param {Roo.EventObject} e
8792          */
8793         "cellclick" : true,
8794         /**
8795          * @event celldblclick
8796          * Fires when a cell is double clicked
8797          * @param {Roo.bootstrap.Table} this
8798          * @param {Roo.Element} el
8799          * @param {Number} rowIndex
8800          * @param {Number} columnIndex
8801          * @param {Roo.EventObject} e
8802          */
8803         "celldblclick" : true,
8804         /**
8805          * @event rowclick
8806          * Fires when a row is clicked
8807          * @param {Roo.bootstrap.Table} this
8808          * @param {Roo.Element} el
8809          * @param {Number} rowIndex
8810          * @param {Roo.EventObject} e
8811          */
8812         "rowclick" : true,
8813         /**
8814          * @event rowdblclick
8815          * Fires when a row is double clicked
8816          * @param {Roo.bootstrap.Table} this
8817          * @param {Roo.Element} el
8818          * @param {Number} rowIndex
8819          * @param {Roo.EventObject} e
8820          */
8821         "rowdblclick" : true,
8822         /**
8823          * @event mouseover
8824          * Fires when a mouseover occur
8825          * @param {Roo.bootstrap.Table} this
8826          * @param {Roo.Element} el
8827          * @param {Number} rowIndex
8828          * @param {Number} columnIndex
8829          * @param {Roo.EventObject} e
8830          */
8831         "mouseover" : true,
8832         /**
8833          * @event mouseout
8834          * Fires when a mouseout occur
8835          * @param {Roo.bootstrap.Table} this
8836          * @param {Roo.Element} el
8837          * @param {Number} rowIndex
8838          * @param {Number} columnIndex
8839          * @param {Roo.EventObject} e
8840          */
8841         "mouseout" : true,
8842         /**
8843          * @event rowclass
8844          * Fires when a row is rendered, so you can change add a style to it.
8845          * @param {Roo.bootstrap.Table} this
8846          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8847          */
8848         'rowclass' : true,
8849           /**
8850          * @event rowsrendered
8851          * Fires when all the  rows have been rendered
8852          * @param {Roo.bootstrap.Table} this
8853          */
8854         'rowsrendered' : true,
8855         /**
8856          * @event contextmenu
8857          * The raw contextmenu event for the entire grid.
8858          * @param {Roo.EventObject} e
8859          */
8860         "contextmenu" : true,
8861         /**
8862          * @event rowcontextmenu
8863          * Fires when a row is right clicked
8864          * @param {Roo.bootstrap.Table} this
8865          * @param {Number} rowIndex
8866          * @param {Roo.EventObject} e
8867          */
8868         "rowcontextmenu" : true,
8869         /**
8870          * @event cellcontextmenu
8871          * Fires when a cell is right clicked
8872          * @param {Roo.bootstrap.Table} this
8873          * @param {Number} rowIndex
8874          * @param {Number} cellIndex
8875          * @param {Roo.EventObject} e
8876          */
8877          "cellcontextmenu" : true,
8878          /**
8879          * @event headercontextmenu
8880          * Fires when a header is right clicked
8881          * @param {Roo.bootstrap.Table} this
8882          * @param {Number} columnIndex
8883          * @param {Roo.EventObject} e
8884          */
8885         "headercontextmenu" : true,
8886         /**
8887          * @event mousedown
8888          * The raw mousedown event for the entire grid.
8889          * @param {Roo.EventObject} e
8890          */
8891         "mousedown" : true
8892         
8893     });
8894 };
8895
8896 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8897     
8898     cls: false,
8899     
8900     striped : false,
8901     scrollBody : false,
8902     bordered: false,
8903     hover:  false,
8904     condensed : false,
8905     responsive : false,
8906     sm : false,
8907     cm : false,
8908     store : false,
8909     loadMask : false,
8910     footerShow : true,
8911     headerShow : true,
8912     enableColumnResize: true,
8913   
8914     rowSelection : false,
8915     cellSelection : false,
8916     layout : false,
8917
8918     minColumnWidth : 50,
8919     
8920     // Roo.Element - the tbody
8921     bodyEl: false,  // <tbody> Roo.Element - thead element    
8922     headEl: false,  // <thead> Roo.Element - thead element
8923     resizeProxy : false, // proxy element for dragging?
8924
8925
8926     
8927     container: false, // used by gridpanel...
8928     
8929     lazyLoad : false,
8930     
8931     CSS : Roo.util.CSS,
8932     
8933     auto_hide_footer : false,
8934     
8935     view: false, // actually points to this..
8936     
8937     getAutoCreate : function()
8938     {
8939         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8940         
8941         cfg = {
8942             tag: 'table',
8943             cls : 'table', 
8944             cn : []
8945         };
8946         // this get's auto added by panel.Grid
8947         if (this.scrollBody) {
8948             cfg.cls += ' table-body-fixed';
8949         }    
8950         if (this.striped) {
8951             cfg.cls += ' table-striped';
8952         }
8953         
8954         if (this.hover) {
8955             cfg.cls += ' table-hover';
8956         }
8957         if (this.bordered) {
8958             cfg.cls += ' table-bordered';
8959         }
8960         if (this.condensed) {
8961             cfg.cls += ' table-condensed';
8962         }
8963         
8964         if (this.responsive) {
8965             cfg.cls += ' table-responsive';
8966         }
8967         
8968         if (this.cls) {
8969             cfg.cls+=  ' ' +this.cls;
8970         }
8971         
8972         
8973         
8974         if (this.layout) {
8975             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8976         }
8977         
8978         if(this.store || this.cm){
8979             if(this.headerShow){
8980                 cfg.cn.push(this.renderHeader());
8981             }
8982             
8983             cfg.cn.push(this.renderBody());
8984             
8985             if(this.footerShow){
8986                 cfg.cn.push(this.renderFooter());
8987             }
8988             // where does this come from?
8989             //cfg.cls+=  ' TableGrid';
8990         }
8991         
8992         return { cn : [ cfg ] };
8993     },
8994     
8995     initEvents : function()
8996     {   
8997         if(!this.store || !this.cm){
8998             return;
8999         }
9000         if (this.selModel) {
9001             this.selModel.initEvents();
9002         }
9003         
9004         
9005         //Roo.log('initEvents with ds!!!!');
9006         
9007         this.bodyEl = this.el.select('tbody', true).first();
9008         this.headEl = this.el.select('thead', true).first();
9009         this.mainFoot = this.el.select('tfoot', true).first();
9010         
9011         
9012         
9013         
9014         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9015             e.on('click', this.sort, this);
9016         }, this);
9017         
9018         
9019         // why is this done????? = it breaks dialogs??
9020         //this.parent().el.setStyle('position', 'relative');
9021         
9022         
9023         if (this.footer) {
9024             this.footer.parentId = this.id;
9025             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9026             
9027             if(this.lazyLoad){
9028                 this.el.select('tfoot tr td').first().addClass('hide');
9029             }
9030         } 
9031         
9032         if(this.loadMask) {
9033             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9034         }
9035         
9036         this.store.on('load', this.onLoad, this);
9037         this.store.on('beforeload', this.onBeforeLoad, this);
9038         this.store.on('update', this.onUpdate, this);
9039         this.store.on('add', this.onAdd, this);
9040         this.store.on("clear", this.clear, this);
9041         
9042         this.el.on("contextmenu", this.onContextMenu, this);
9043         
9044         
9045         this.cm.on("headerchange", this.onHeaderChange, this);
9046         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9047
9048  //?? does bodyEl get replaced on render?
9049         this.bodyEl.on("click", this.onClick, this);
9050         this.bodyEl.on("dblclick", this.onDblClick, this);        
9051         this.bodyEl.on('scroll', this.onBodyScroll, this);
9052
9053         // guessing mainbody will work - this relays usually caught by selmodel at present.
9054         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9055   
9056   
9057         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9058         
9059   
9060         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9061             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9062         }
9063         
9064         this.initCSS();
9065     },
9066     // Compatibility with grid - we implement all the view features at present.
9067     getView : function()
9068     {
9069         return this;
9070     },
9071     
9072     initCSS : function()
9073     {
9074         
9075         
9076         var cm = this.cm, styles = [];
9077         this.CSS.removeStyleSheet(this.id + '-cssrules');
9078         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9079         // we can honour xs/sm/md/xl  as widths...
9080         // we first have to decide what widht we are currently at...
9081         var sz = Roo.getGridSize();
9082         
9083         var total = 0;
9084         var last = -1;
9085         var cols = []; // visable cols.
9086         var total_abs = 0;
9087         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9088             var w = cm.getColumnWidth(i, false);
9089             if(cm.isHidden(i)){
9090                 cols.push( { rel : false, abs : 0 });
9091                 continue;
9092             }
9093             if (w !== false) {
9094                 cols.push( { rel : false, abs : w });
9095                 total_abs += w;
9096                 last = i; // not really..
9097                 continue;
9098             }
9099             var w = cm.getColumnWidth(i, sz);
9100             if (w > 0) {
9101                 last = i
9102             }
9103             total += w;
9104             cols.push( { rel : w, abs : false });
9105         }
9106         
9107         var avail = this.bodyEl.dom.clientWidth - total_abs;
9108         
9109         var unitWidth = Math.floor(avail / total);
9110         var rem = avail - (unitWidth * total);
9111         
9112         var hidden, width, pos = 0 , splithide , left;
9113         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9114             
9115             hidden = 'display:none;';
9116             left = '';
9117             width  = 'width:0px;';
9118             splithide = '';
9119             if(!cm.isHidden(i)){
9120                 hidden = '';
9121                 
9122                 
9123                 // we can honour xs/sm/md/xl ?
9124                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9125                 if (w===0) {
9126                     hidden = 'display:none;';
9127                 }
9128                 // width should return a small number...
9129                 if (i == last) {
9130                     w+=rem; // add the remaining with..
9131                 }
9132                 pos += w;
9133                 left = "left:" + (pos -4) + "px;";
9134                 width = "width:" + w+ "px;";
9135                 
9136             }
9137             if (this.responsive) {
9138                 width = '';
9139                 left = '';
9140                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9141                 splithide = 'display: none;';
9142             }
9143             
9144             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9145             if (this.headEl) {
9146                 if (i == last) {
9147                     splithide = 'display:none;';
9148                 }
9149                 
9150                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9151                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9152                 );
9153             }
9154             
9155         }
9156         //Roo.log(styles.join(''));
9157         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9158         
9159     },
9160     
9161     
9162     
9163     onContextMenu : function(e, t)
9164     {
9165         this.processEvent("contextmenu", e);
9166     },
9167     
9168     processEvent : function(name, e)
9169     {
9170         if (name != 'touchstart' ) {
9171             this.fireEvent(name, e);    
9172         }
9173         
9174         var t = e.getTarget();
9175         
9176         var cell = Roo.get(t);
9177         
9178         if(!cell){
9179             return;
9180         }
9181         
9182         if(cell.findParent('tfoot', false, true)){
9183             return;
9184         }
9185         
9186         if(cell.findParent('thead', false, true)){
9187             
9188             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9189                 cell = Roo.get(t).findParent('th', false, true);
9190                 if (!cell) {
9191                     Roo.log("failed to find th in thead?");
9192                     Roo.log(e.getTarget());
9193                     return;
9194                 }
9195             }
9196             
9197             var cellIndex = cell.dom.cellIndex;
9198             
9199             var ename = name == 'touchstart' ? 'click' : name;
9200             this.fireEvent("header" + ename, this, cellIndex, e);
9201             
9202             return;
9203         }
9204         
9205         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9206             cell = Roo.get(t).findParent('td', false, true);
9207             if (!cell) {
9208                 Roo.log("failed to find th in tbody?");
9209                 Roo.log(e.getTarget());
9210                 return;
9211             }
9212         }
9213         
9214         var row = cell.findParent('tr', false, true);
9215         var cellIndex = cell.dom.cellIndex;
9216         var rowIndex = row.dom.rowIndex - 1;
9217         
9218         if(row !== false){
9219             
9220             this.fireEvent("row" + name, this, rowIndex, e);
9221             
9222             if(cell !== false){
9223             
9224                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9225             }
9226         }
9227         
9228     },
9229     
9230     onMouseover : function(e, el)
9231     {
9232         var cell = Roo.get(el);
9233         
9234         if(!cell){
9235             return;
9236         }
9237         
9238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9239             cell = cell.findParent('td', false, true);
9240         }
9241         
9242         var row = cell.findParent('tr', false, true);
9243         var cellIndex = cell.dom.cellIndex;
9244         var rowIndex = row.dom.rowIndex - 1; // start from 0
9245         
9246         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9247         
9248     },
9249     
9250     onMouseout : function(e, el)
9251     {
9252         var cell = Roo.get(el);
9253         
9254         if(!cell){
9255             return;
9256         }
9257         
9258         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9259             cell = cell.findParent('td', false, true);
9260         }
9261         
9262         var row = cell.findParent('tr', false, true);
9263         var cellIndex = cell.dom.cellIndex;
9264         var rowIndex = row.dom.rowIndex - 1; // start from 0
9265         
9266         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9267         
9268     },
9269     
9270     onClick : function(e, el)
9271     {
9272         var cell = Roo.get(el);
9273         
9274         if(!cell || (!this.cellSelection && !this.rowSelection)){
9275             return;
9276         }
9277         
9278         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9279             cell = cell.findParent('td', false, true);
9280         }
9281         
9282         if(!cell || typeof(cell) == 'undefined'){
9283             return;
9284         }
9285         
9286         var row = cell.findParent('tr', false, true);
9287         
9288         if(!row || typeof(row) == 'undefined'){
9289             return;
9290         }
9291         
9292         var cellIndex = cell.dom.cellIndex;
9293         var rowIndex = this.getRowIndex(row);
9294         
9295         // why??? - should these not be based on SelectionModel?
9296         //if(this.cellSelection){
9297             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9298         //}
9299         
9300         //if(this.rowSelection){
9301             this.fireEvent('rowclick', this, row, rowIndex, e);
9302         //}
9303          
9304     },
9305         
9306     onDblClick : function(e,el)
9307     {
9308         var cell = Roo.get(el);
9309         
9310         if(!cell || (!this.cellSelection && !this.rowSelection)){
9311             return;
9312         }
9313         
9314         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9315             cell = cell.findParent('td', false, true);
9316         }
9317         
9318         if(!cell || typeof(cell) == 'undefined'){
9319             return;
9320         }
9321         
9322         var row = cell.findParent('tr', false, true);
9323         
9324         if(!row || typeof(row) == 'undefined'){
9325             return;
9326         }
9327         
9328         var cellIndex = cell.dom.cellIndex;
9329         var rowIndex = this.getRowIndex(row);
9330         
9331         if(this.cellSelection){
9332             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9333         }
9334         
9335         if(this.rowSelection){
9336             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9337         }
9338     },
9339     findRowIndex : function(el)
9340     {
9341         var cell = Roo.get(el);
9342         if(!cell) {
9343             return false;
9344         }
9345         var row = cell.findParent('tr', false, true);
9346         
9347         if(!row || typeof(row) == 'undefined'){
9348             return false;
9349         }
9350         return this.getRowIndex(row);
9351     },
9352     sort : function(e,el)
9353     {
9354         var col = Roo.get(el);
9355         
9356         if(!col.hasClass('sortable')){
9357             return;
9358         }
9359         
9360         var sort = col.attr('sort');
9361         var dir = 'ASC';
9362         
9363         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9364             dir = 'DESC';
9365         }
9366         
9367         this.store.sortInfo = {field : sort, direction : dir};
9368         
9369         if (this.footer) {
9370             Roo.log("calling footer first");
9371             this.footer.onClick('first');
9372         } else {
9373         
9374             this.store.load({ params : { start : 0 } });
9375         }
9376     },
9377     
9378     renderHeader : function()
9379     {
9380         var header = {
9381             tag: 'thead',
9382             cn : []
9383         };
9384         
9385         var cm = this.cm;
9386         this.totalWidth = 0;
9387         
9388         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9389             
9390             var config = cm.config[i];
9391             
9392             var c = {
9393                 tag: 'th',
9394                 cls : 'x-hcol-' + i,
9395                 style : '',
9396                 
9397                 html: cm.getColumnHeader(i)
9398             };
9399             
9400             var tooltip = cm.getColumnTooltip(i);
9401             if (tooltip) {
9402                 c.tooltip = tooltip;
9403             }
9404             
9405             
9406             var hh = '';
9407             
9408             if(typeof(config.sortable) != 'undefined' && config.sortable){
9409                 c.cls += ' sortable';
9410                 c.html = '<i class="fa"></i>' + c.html;
9411             }
9412             
9413             // could use BS4 hidden-..-down 
9414             
9415             if(typeof(config.lgHeader) != 'undefined'){
9416                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9417             }
9418             
9419             if(typeof(config.mdHeader) != 'undefined'){
9420                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9421             }
9422             
9423             if(typeof(config.smHeader) != 'undefined'){
9424                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9425             }
9426             
9427             if(typeof(config.xsHeader) != 'undefined'){
9428                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9429             }
9430             
9431             if(hh.length){
9432                 c.html = hh;
9433             }
9434             
9435             if(typeof(config.tooltip) != 'undefined'){
9436                 c.tooltip = config.tooltip;
9437             }
9438             
9439             if(typeof(config.colspan) != 'undefined'){
9440                 c.colspan = config.colspan;
9441             }
9442             
9443             // hidden is handled by CSS now
9444             
9445             if(typeof(config.dataIndex) != 'undefined'){
9446                 c.sort = config.dataIndex;
9447             }
9448             
9449            
9450             
9451             if(typeof(config.align) != 'undefined' && config.align.length){
9452                 c.style += ' text-align:' + config.align + ';';
9453             }
9454             
9455             /* width is done in CSS
9456              *if(typeof(config.width) != 'undefined'){
9457                 c.style += ' width:' + config.width + 'px;';
9458                 this.totalWidth += config.width;
9459             } else {
9460                 this.totalWidth += 100; // assume minimum of 100 per column?
9461             }
9462             */
9463             
9464             if(typeof(config.cls) != 'undefined'){
9465                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9466             }
9467             // this is the bit that doesnt reall work at all...
9468             
9469             if (this.responsive) {
9470                  
9471             
9472                 ['xs','sm','md','lg'].map(function(size){
9473                     
9474                     if(typeof(config[size]) == 'undefined'){
9475                         return;
9476                     }
9477                      
9478                     if (!config[size]) { // 0 = hidden
9479                         // BS 4 '0' is treated as hide that column and below.
9480                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9481                         return;
9482                     }
9483                     
9484                     c.cls += ' col-' + size + '-' + config[size] + (
9485                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9486                     );
9487                     
9488                     
9489                 });
9490             }
9491             // at the end?
9492             
9493             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9494             
9495             
9496             
9497             
9498             header.cn.push(c)
9499         }
9500         
9501         return header;
9502     },
9503     
9504     renderBody : function()
9505     {
9506         var body = {
9507             tag: 'tbody',
9508             cn : [
9509                 {
9510                     tag: 'tr',
9511                     cn : [
9512                         {
9513                             tag : 'td',
9514                             colspan :  this.cm.getColumnCount()
9515                         }
9516                     ]
9517                 }
9518             ]
9519         };
9520         
9521         return body;
9522     },
9523     
9524     renderFooter : function()
9525     {
9526         var footer = {
9527             tag: 'tfoot',
9528             cn : [
9529                 {
9530                     tag: 'tr',
9531                     cn : [
9532                         {
9533                             tag : 'td',
9534                             colspan :  this.cm.getColumnCount()
9535                         }
9536                     ]
9537                 }
9538             ]
9539         };
9540         
9541         return footer;
9542     },
9543     
9544     
9545     
9546     onLoad : function()
9547     {
9548 //        Roo.log('ds onload');
9549         this.clear();
9550         
9551         var _this = this;
9552         var cm = this.cm;
9553         var ds = this.store;
9554         
9555         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9556             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9557             if (_this.store.sortInfo) {
9558                     
9559                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9560                     e.select('i', true).addClass(['fa-arrow-up']);
9561                 }
9562                 
9563                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9564                     e.select('i', true).addClass(['fa-arrow-down']);
9565                 }
9566             }
9567         });
9568         
9569         var tbody =  this.bodyEl;
9570               
9571         if(ds.getCount() > 0){
9572             ds.data.each(function(d,rowIndex){
9573                 var row =  this.renderRow(cm, ds, rowIndex);
9574                 
9575                 tbody.createChild(row);
9576                 
9577                 var _this = this;
9578                 
9579                 if(row.cellObjects.length){
9580                     Roo.each(row.cellObjects, function(r){
9581                         _this.renderCellObject(r);
9582                     })
9583                 }
9584                 
9585             }, this);
9586         }
9587         
9588         var tfoot = this.el.select('tfoot', true).first();
9589         
9590         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9591             
9592             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9593             
9594             var total = this.ds.getTotalCount();
9595             
9596             if(this.footer.pageSize < total){
9597                 this.mainFoot.show();
9598             }
9599         }
9600         
9601         Roo.each(this.el.select('tbody td', true).elements, function(e){
9602             e.on('mouseover', _this.onMouseover, _this);
9603         });
9604         
9605         Roo.each(this.el.select('tbody td', true).elements, function(e){
9606             e.on('mouseout', _this.onMouseout, _this);
9607         });
9608         this.fireEvent('rowsrendered', this);
9609         
9610         this.autoSize();
9611         
9612         this.initCSS(); /// resize cols
9613
9614         
9615     },
9616     
9617     
9618     onUpdate : function(ds,record)
9619     {
9620         this.refreshRow(record);
9621         this.autoSize();
9622     },
9623     
9624     onRemove : function(ds, record, index, isUpdate){
9625         if(isUpdate !== true){
9626             this.fireEvent("beforerowremoved", this, index, record);
9627         }
9628         var bt = this.bodyEl.dom;
9629         
9630         var rows = this.el.select('tbody > tr', true).elements;
9631         
9632         if(typeof(rows[index]) != 'undefined'){
9633             bt.removeChild(rows[index].dom);
9634         }
9635         
9636 //        if(bt.rows[index]){
9637 //            bt.removeChild(bt.rows[index]);
9638 //        }
9639         
9640         if(isUpdate !== true){
9641             //this.stripeRows(index);
9642             //this.syncRowHeights(index, index);
9643             //this.layout();
9644             this.fireEvent("rowremoved", this, index, record);
9645         }
9646     },
9647     
9648     onAdd : function(ds, records, rowIndex)
9649     {
9650         //Roo.log('on Add called');
9651         // - note this does not handle multiple adding very well..
9652         var bt = this.bodyEl.dom;
9653         for (var i =0 ; i < records.length;i++) {
9654             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9655             //Roo.log(records[i]);
9656             //Roo.log(this.store.getAt(rowIndex+i));
9657             this.insertRow(this.store, rowIndex + i, false);
9658             return;
9659         }
9660         
9661     },
9662     
9663     
9664     refreshRow : function(record){
9665         var ds = this.store, index;
9666         if(typeof record == 'number'){
9667             index = record;
9668             record = ds.getAt(index);
9669         }else{
9670             index = ds.indexOf(record);
9671             if (index < 0) {
9672                 return; // should not happen - but seems to 
9673             }
9674         }
9675         this.insertRow(ds, index, true);
9676         this.autoSize();
9677         this.onRemove(ds, record, index+1, true);
9678         this.autoSize();
9679         //this.syncRowHeights(index, index);
9680         //this.layout();
9681         this.fireEvent("rowupdated", this, index, record);
9682     },
9683     // private - called by RowSelection
9684     onRowSelect : function(rowIndex){
9685         var row = this.getRowDom(rowIndex);
9686         row.addClass(['bg-info','info']);
9687     },
9688     // private - called by RowSelection
9689     onRowDeselect : function(rowIndex)
9690     {
9691         if (rowIndex < 0) {
9692             return;
9693         }
9694         var row = this.getRowDom(rowIndex);
9695         row.removeClass(['bg-info','info']);
9696     },
9697       /**
9698      * Focuses the specified row.
9699      * @param {Number} row The row index
9700      */
9701     focusRow : function(row)
9702     {
9703         //Roo.log('GridView.focusRow');
9704         var x = this.bodyEl.dom.scrollLeft;
9705         this.focusCell(row, 0, false);
9706         this.bodyEl.dom.scrollLeft = x;
9707
9708     },
9709      /**
9710      * Focuses the specified cell.
9711      * @param {Number} row The row index
9712      * @param {Number} col The column index
9713      * @param {Boolean} hscroll false to disable horizontal scrolling
9714      */
9715     focusCell : function(row, col, hscroll)
9716     {
9717         //Roo.log('GridView.focusCell');
9718         var el = this.ensureVisible(row, col, hscroll);
9719         // not sure what focusEL achives = it's a <a> pos relative 
9720         //this.focusEl.alignTo(el, "tl-tl");
9721         //if(Roo.isGecko){
9722         //    this.focusEl.focus();
9723         //}else{
9724         //    this.focusEl.focus.defer(1, this.focusEl);
9725         //}
9726     },
9727     
9728      /**
9729      * Scrolls the specified cell into view
9730      * @param {Number} row The row index
9731      * @param {Number} col The column index
9732      * @param {Boolean} hscroll false to disable horizontal scrolling
9733      */
9734     ensureVisible : function(row, col, hscroll)
9735     {
9736         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9737         //return null; //disable for testing.
9738         if(typeof row != "number"){
9739             row = row.rowIndex;
9740         }
9741         if(row < 0 && row >= this.ds.getCount()){
9742             return  null;
9743         }
9744         col = (col !== undefined ? col : 0);
9745         var cm = this.cm;
9746         while(cm.isHidden(col)){
9747             col++;
9748         }
9749
9750         var el = this.getCellDom(row, col);
9751         if(!el){
9752             return null;
9753         }
9754         var c = this.bodyEl.dom;
9755
9756         var ctop = parseInt(el.offsetTop, 10);
9757         var cleft = parseInt(el.offsetLeft, 10);
9758         var cbot = ctop + el.offsetHeight;
9759         var cright = cleft + el.offsetWidth;
9760
9761         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9762         var ch = 0; //?? header is not withing the area?
9763         var stop = parseInt(c.scrollTop, 10);
9764         var sleft = parseInt(c.scrollLeft, 10);
9765         var sbot = stop + ch;
9766         var sright = sleft + c.clientWidth;
9767         /*
9768         Roo.log('GridView.ensureVisible:' +
9769                 ' ctop:' + ctop +
9770                 ' c.clientHeight:' + c.clientHeight +
9771                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9772                 ' stop:' + stop +
9773                 ' cbot:' + cbot +
9774                 ' sbot:' + sbot +
9775                 ' ch:' + ch  
9776                 );
9777         */
9778         if(ctop < stop){
9779             c.scrollTop = ctop;
9780             //Roo.log("set scrolltop to ctop DISABLE?");
9781         }else if(cbot > sbot){
9782             //Roo.log("set scrolltop to cbot-ch");
9783             c.scrollTop = cbot-ch;
9784         }
9785
9786         if(hscroll !== false){
9787             if(cleft < sleft){
9788                 c.scrollLeft = cleft;
9789             }else if(cright > sright){
9790                 c.scrollLeft = cright-c.clientWidth;
9791             }
9792         }
9793
9794         return el;
9795     },
9796     
9797     
9798     insertRow : function(dm, rowIndex, isUpdate){
9799         
9800         if(!isUpdate){
9801             this.fireEvent("beforerowsinserted", this, rowIndex);
9802         }
9803             //var s = this.getScrollState();
9804         var row = this.renderRow(this.cm, this.store, rowIndex);
9805         // insert before rowIndex..
9806         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9807         
9808         var _this = this;
9809                 
9810         if(row.cellObjects.length){
9811             Roo.each(row.cellObjects, function(r){
9812                 _this.renderCellObject(r);
9813             })
9814         }
9815             
9816         if(!isUpdate){
9817             this.fireEvent("rowsinserted", this, rowIndex);
9818             //this.syncRowHeights(firstRow, lastRow);
9819             //this.stripeRows(firstRow);
9820             //this.layout();
9821         }
9822         
9823     },
9824     
9825     
9826     getRowDom : function(rowIndex)
9827     {
9828         var rows = this.el.select('tbody > tr', true).elements;
9829         
9830         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9831         
9832     },
9833     getCellDom : function(rowIndex, colIndex)
9834     {
9835         var row = this.getRowDom(rowIndex);
9836         if (row === false) {
9837             return false;
9838         }
9839         var cols = row.select('td', true).elements;
9840         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9841         
9842     },
9843     
9844     // returns the object tree for a tr..
9845   
9846     
9847     renderRow : function(cm, ds, rowIndex) 
9848     {
9849         var d = ds.getAt(rowIndex);
9850         
9851         var row = {
9852             tag : 'tr',
9853             cls : 'x-row-' + rowIndex,
9854             cn : []
9855         };
9856             
9857         var cellObjects = [];
9858         
9859         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9860             var config = cm.config[i];
9861             
9862             var renderer = cm.getRenderer(i);
9863             var value = '';
9864             var id = false;
9865             
9866             if(typeof(renderer) !== 'undefined'){
9867                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9868             }
9869             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9870             // and are rendered into the cells after the row is rendered - using the id for the element.
9871             
9872             if(typeof(value) === 'object'){
9873                 id = Roo.id();
9874                 cellObjects.push({
9875                     container : id,
9876                     cfg : value 
9877                 })
9878             }
9879             
9880             var rowcfg = {
9881                 record: d,
9882                 rowIndex : rowIndex,
9883                 colIndex : i,
9884                 rowClass : ''
9885             };
9886
9887             this.fireEvent('rowclass', this, rowcfg);
9888             
9889             var td = {
9890                 tag: 'td',
9891                 // this might end up displaying HTML?
9892                 // this is too messy... - better to only do it on columsn you know are going to be too long
9893                 //tooltip : (typeof(value) === 'object') ? '' : value,
9894                 cls : rowcfg.rowClass + ' x-col-' + i,
9895                 style: '',
9896                 html: (typeof(value) === 'object') ? '' : value
9897             };
9898             
9899             if (id) {
9900                 td.id = id;
9901             }
9902             
9903             if(typeof(config.colspan) != 'undefined'){
9904                 td.colspan = config.colspan;
9905             }
9906             
9907             
9908             
9909             if(typeof(config.align) != 'undefined' && config.align.length){
9910                 td.style += ' text-align:' + config.align + ';';
9911             }
9912             if(typeof(config.valign) != 'undefined' && config.valign.length){
9913                 td.style += ' vertical-align:' + config.valign + ';';
9914             }
9915             /*
9916             if(typeof(config.width) != 'undefined'){
9917                 td.style += ' width:' +  config.width + 'px;';
9918             }
9919             */
9920             
9921             if(typeof(config.cursor) != 'undefined'){
9922                 td.style += ' cursor:' +  config.cursor + ';';
9923             }
9924             
9925             if(typeof(config.cls) != 'undefined'){
9926                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9927             }
9928             if (this.responsive) {
9929                 ['xs','sm','md','lg'].map(function(size){
9930                     
9931                     if(typeof(config[size]) == 'undefined'){
9932                         return;
9933                     }
9934                     
9935                     
9936                       
9937                     if (!config[size]) { // 0 = hidden
9938                         // BS 4 '0' is treated as hide that column and below.
9939                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9940                         return;
9941                     }
9942                     
9943                     td.cls += ' col-' + size + '-' + config[size] + (
9944                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9945                     );
9946                      
9947     
9948                 });
9949             }
9950             row.cn.push(td);
9951            
9952         }
9953         
9954         row.cellObjects = cellObjects;
9955         
9956         return row;
9957           
9958     },
9959     
9960     
9961     
9962     onBeforeLoad : function()
9963     {
9964         
9965     },
9966      /**
9967      * Remove all rows
9968      */
9969     clear : function()
9970     {
9971         this.el.select('tbody', true).first().dom.innerHTML = '';
9972     },
9973     /**
9974      * Show or hide a row.
9975      * @param {Number} rowIndex to show or hide
9976      * @param {Boolean} state hide
9977      */
9978     setRowVisibility : function(rowIndex, state)
9979     {
9980         var bt = this.bodyEl.dom;
9981         
9982         var rows = this.el.select('tbody > tr', true).elements;
9983         
9984         if(typeof(rows[rowIndex]) == 'undefined'){
9985             return;
9986         }
9987         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9988         
9989     },
9990     
9991     
9992     getSelectionModel : function(){
9993         if(!this.selModel){
9994             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9995         }
9996         return this.selModel;
9997     },
9998     /*
9999      * Render the Roo.bootstrap object from renderder
10000      */
10001     renderCellObject : function(r)
10002     {
10003         var _this = this;
10004         
10005         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10006         
10007         var t = r.cfg.render(r.container);
10008         
10009         if(r.cfg.cn){
10010             Roo.each(r.cfg.cn, function(c){
10011                 var child = {
10012                     container: t.getChildContainer(),
10013                     cfg: c
10014                 };
10015                 _this.renderCellObject(child);
10016             })
10017         }
10018     },
10019     /**
10020      * get the Row Index from a dom element.
10021      * @param {Roo.Element} row The row to look for
10022      * @returns {Number} the row
10023      */
10024     getRowIndex : function(row)
10025     {
10026         var rowIndex = -1;
10027         
10028         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10029             if(el != row){
10030                 return;
10031             }
10032             
10033             rowIndex = index;
10034         });
10035         
10036         return rowIndex;
10037     },
10038     /**
10039      * get the header TH element for columnIndex
10040      * @param {Number} columnIndex
10041      * @returns {Roo.Element}
10042      */
10043     getHeaderIndex: function(colIndex)
10044     {
10045         var cols = this.headEl.select('th', true).elements;
10046         return cols[colIndex]; 
10047     },
10048     /**
10049      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10050      * @param {domElement} cell to look for
10051      * @returns {Number} the column
10052      */
10053     getCellIndex : function(cell)
10054     {
10055         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10056         if(id){
10057             return parseInt(id[1], 10);
10058         }
10059         return 0;
10060     },
10061      /**
10062      * Returns the grid's underlying element = used by panel.Grid
10063      * @return {Element} The element
10064      */
10065     getGridEl : function(){
10066         return this.el;
10067     },
10068      /**
10069      * Forces a resize - used by panel.Grid
10070      * @return {Element} The element
10071      */
10072     autoSize : function()
10073     {
10074         //var ctr = Roo.get(this.container.dom.parentElement);
10075         var ctr = Roo.get(this.el.dom);
10076         
10077         var thd = this.getGridEl().select('thead',true).first();
10078         var tbd = this.getGridEl().select('tbody', true).first();
10079         var tfd = this.getGridEl().select('tfoot', true).first();
10080         
10081         var cw = ctr.getWidth();
10082         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10083         
10084         if (tbd) {
10085             
10086             tbd.setWidth(ctr.getWidth());
10087             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10088             // this needs fixing for various usage - currently only hydra job advers I think..
10089             //tdb.setHeight(
10090             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10091             //); 
10092             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10093             cw -= barsize;
10094         }
10095         cw = Math.max(cw, this.totalWidth);
10096         this.getGridEl().select('tbody tr',true).setWidth(cw);
10097         this.initCSS();
10098         
10099         // resize 'expandable coloumn?
10100         
10101         return; // we doe not have a view in this design..
10102         
10103     },
10104     onBodyScroll: function()
10105     {
10106         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10107         if(this.headEl){
10108             this.headEl.setStyle({
10109                 'position' : 'relative',
10110                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10111             });
10112         }
10113         
10114         if(this.lazyLoad){
10115             
10116             var scrollHeight = this.bodyEl.dom.scrollHeight;
10117             
10118             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10119             
10120             var height = this.bodyEl.getHeight();
10121             
10122             if(scrollHeight - height == scrollTop) {
10123                 
10124                 var total = this.ds.getTotalCount();
10125                 
10126                 if(this.footer.cursor + this.footer.pageSize < total){
10127                     
10128                     this.footer.ds.load({
10129                         params : {
10130                             start : this.footer.cursor + this.footer.pageSize,
10131                             limit : this.footer.pageSize
10132                         },
10133                         add : true
10134                     });
10135                 }
10136             }
10137             
10138         }
10139     },
10140     onColumnSplitterMoved : function(i, diff)
10141     {
10142         this.userResized = true;
10143         
10144         var cm = this.colModel;
10145         
10146         var w = this.getHeaderIndex(i).getWidth() + diff;
10147         
10148         
10149         cm.setColumnWidth(i, w, true);
10150         this.initCSS();
10151         //var cid = cm.getColumnId(i); << not used in this version?
10152        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10153         
10154         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10155         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10156         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10157 */
10158         //this.updateSplitters();
10159         //this.layout(); << ??
10160         this.fireEvent("columnresize", i, w);
10161     },
10162     onHeaderChange : function()
10163     {
10164         var header = this.renderHeader();
10165         var table = this.el.select('table', true).first();
10166         
10167         this.headEl.remove();
10168         this.headEl = table.createChild(header, this.bodyEl, false);
10169         
10170         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10171             e.on('click', this.sort, this);
10172         }, this);
10173         
10174         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10175             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10176         }
10177         
10178     },
10179     
10180     onHiddenChange : function(colModel, colIndex, hidden)
10181     {
10182         /*
10183         this.cm.setHidden()
10184         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10185         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10186         
10187         this.CSS.updateRule(thSelector, "display", "");
10188         this.CSS.updateRule(tdSelector, "display", "");
10189         
10190         if(hidden){
10191             this.CSS.updateRule(thSelector, "display", "none");
10192             this.CSS.updateRule(tdSelector, "display", "none");
10193         }
10194         */
10195         // onload calls initCSS()
10196         this.onHeaderChange();
10197         this.onLoad();
10198     },
10199     
10200     setColumnWidth: function(col_index, width)
10201     {
10202         // width = "md-2 xs-2..."
10203         if(!this.colModel.config[col_index]) {
10204             return;
10205         }
10206         
10207         var w = width.split(" ");
10208         
10209         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10210         
10211         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10212         
10213         
10214         for(var j = 0; j < w.length; j++) {
10215             
10216             if(!w[j]) {
10217                 continue;
10218             }
10219             
10220             var size_cls = w[j].split("-");
10221             
10222             if(!Number.isInteger(size_cls[1] * 1)) {
10223                 continue;
10224             }
10225             
10226             if(!this.colModel.config[col_index][size_cls[0]]) {
10227                 continue;
10228             }
10229             
10230             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10231                 continue;
10232             }
10233             
10234             h_row[0].classList.replace(
10235                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10236                 "col-"+size_cls[0]+"-"+size_cls[1]
10237             );
10238             
10239             for(var i = 0; i < rows.length; i++) {
10240                 
10241                 var size_cls = w[j].split("-");
10242                 
10243                 if(!Number.isInteger(size_cls[1] * 1)) {
10244                     continue;
10245                 }
10246                 
10247                 if(!this.colModel.config[col_index][size_cls[0]]) {
10248                     continue;
10249                 }
10250                 
10251                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10252                     continue;
10253                 }
10254                 
10255                 rows[i].classList.replace(
10256                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10257                     "col-"+size_cls[0]+"-"+size_cls[1]
10258                 );
10259             }
10260             
10261             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10262         }
10263     }
10264 });
10265
10266 // currently only used to find the split on drag.. 
10267 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10268
10269 /**
10270  * @depricated
10271 */
10272 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10273 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10274 /*
10275  * - LGPL
10276  *
10277  * table cell
10278  * 
10279  */
10280
10281 /**
10282  * @class Roo.bootstrap.TableCell
10283  * @extends Roo.bootstrap.Component
10284  * Bootstrap TableCell class
10285  * @cfg {String} html cell contain text
10286  * @cfg {String} cls cell class
10287  * @cfg {String} tag cell tag (td|th) default td
10288  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10289  * @cfg {String} align Aligns the content in a cell
10290  * @cfg {String} axis Categorizes cells
10291  * @cfg {String} bgcolor Specifies the background color of a cell
10292  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10293  * @cfg {Number} colspan Specifies the number of columns a cell should span
10294  * @cfg {String} headers Specifies one or more header cells a cell is related to
10295  * @cfg {Number} height Sets the height of a cell
10296  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10297  * @cfg {Number} rowspan Sets the number of rows a cell should span
10298  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10299  * @cfg {String} valign Vertical aligns the content in a cell
10300  * @cfg {Number} width Specifies the width of a cell
10301  * 
10302  * @constructor
10303  * Create a new TableCell
10304  * @param {Object} config The config object
10305  */
10306
10307 Roo.bootstrap.TableCell = function(config){
10308     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10309 };
10310
10311 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10312     
10313     html: false,
10314     cls: false,
10315     tag: false,
10316     abbr: false,
10317     align: false,
10318     axis: false,
10319     bgcolor: false,
10320     charoff: false,
10321     colspan: false,
10322     headers: false,
10323     height: false,
10324     nowrap: false,
10325     rowspan: false,
10326     scope: false,
10327     valign: false,
10328     width: false,
10329     
10330     
10331     getAutoCreate : function(){
10332         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10333         
10334         cfg = {
10335             tag: 'td'
10336         };
10337         
10338         if(this.tag){
10339             cfg.tag = this.tag;
10340         }
10341         
10342         if (this.html) {
10343             cfg.html=this.html
10344         }
10345         if (this.cls) {
10346             cfg.cls=this.cls
10347         }
10348         if (this.abbr) {
10349             cfg.abbr=this.abbr
10350         }
10351         if (this.align) {
10352             cfg.align=this.align
10353         }
10354         if (this.axis) {
10355             cfg.axis=this.axis
10356         }
10357         if (this.bgcolor) {
10358             cfg.bgcolor=this.bgcolor
10359         }
10360         if (this.charoff) {
10361             cfg.charoff=this.charoff
10362         }
10363         if (this.colspan) {
10364             cfg.colspan=this.colspan
10365         }
10366         if (this.headers) {
10367             cfg.headers=this.headers
10368         }
10369         if (this.height) {
10370             cfg.height=this.height
10371         }
10372         if (this.nowrap) {
10373             cfg.nowrap=this.nowrap
10374         }
10375         if (this.rowspan) {
10376             cfg.rowspan=this.rowspan
10377         }
10378         if (this.scope) {
10379             cfg.scope=this.scope
10380         }
10381         if (this.valign) {
10382             cfg.valign=this.valign
10383         }
10384         if (this.width) {
10385             cfg.width=this.width
10386         }
10387         
10388         
10389         return cfg;
10390     }
10391    
10392 });
10393
10394  
10395
10396  /*
10397  * - LGPL
10398  *
10399  * table row
10400  * 
10401  */
10402
10403 /**
10404  * @class Roo.bootstrap.TableRow
10405  * @extends Roo.bootstrap.Component
10406  * Bootstrap TableRow class
10407  * @cfg {String} cls row class
10408  * @cfg {String} align Aligns the content in a table row
10409  * @cfg {String} bgcolor Specifies a background color for a table row
10410  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10411  * @cfg {String} valign Vertical aligns the content in a table row
10412  * 
10413  * @constructor
10414  * Create a new TableRow
10415  * @param {Object} config The config object
10416  */
10417
10418 Roo.bootstrap.TableRow = function(config){
10419     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10420 };
10421
10422 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10423     
10424     cls: false,
10425     align: false,
10426     bgcolor: false,
10427     charoff: false,
10428     valign: false,
10429     
10430     getAutoCreate : function(){
10431         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10432         
10433         cfg = {
10434             tag: 'tr'
10435         };
10436             
10437         if(this.cls){
10438             cfg.cls = this.cls;
10439         }
10440         if(this.align){
10441             cfg.align = this.align;
10442         }
10443         if(this.bgcolor){
10444             cfg.bgcolor = this.bgcolor;
10445         }
10446         if(this.charoff){
10447             cfg.charoff = this.charoff;
10448         }
10449         if(this.valign){
10450             cfg.valign = this.valign;
10451         }
10452         
10453         return cfg;
10454     }
10455    
10456 });
10457
10458  
10459
10460  /*
10461  * - LGPL
10462  *
10463  * table body
10464  * 
10465  */
10466
10467 /**
10468  * @class Roo.bootstrap.TableBody
10469  * @extends Roo.bootstrap.Component
10470  * Bootstrap TableBody class
10471  * @cfg {String} cls element class
10472  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10473  * @cfg {String} align Aligns the content inside the element
10474  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10475  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10476  * 
10477  * @constructor
10478  * Create a new TableBody
10479  * @param {Object} config The config object
10480  */
10481
10482 Roo.bootstrap.TableBody = function(config){
10483     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10484 };
10485
10486 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10487     
10488     cls: false,
10489     tag: false,
10490     align: false,
10491     charoff: false,
10492     valign: false,
10493     
10494     getAutoCreate : function(){
10495         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10496         
10497         cfg = {
10498             tag: 'tbody'
10499         };
10500             
10501         if (this.cls) {
10502             cfg.cls=this.cls
10503         }
10504         if(this.tag){
10505             cfg.tag = this.tag;
10506         }
10507         
10508         if(this.align){
10509             cfg.align = this.align;
10510         }
10511         if(this.charoff){
10512             cfg.charoff = this.charoff;
10513         }
10514         if(this.valign){
10515             cfg.valign = this.valign;
10516         }
10517         
10518         return cfg;
10519     }
10520     
10521     
10522 //    initEvents : function()
10523 //    {
10524 //        
10525 //        if(!this.store){
10526 //            return;
10527 //        }
10528 //        
10529 //        this.store = Roo.factory(this.store, Roo.data);
10530 //        this.store.on('load', this.onLoad, this);
10531 //        
10532 //        this.store.load();
10533 //        
10534 //    },
10535 //    
10536 //    onLoad: function () 
10537 //    {   
10538 //        this.fireEvent('load', this);
10539 //    }
10540 //    
10541 //   
10542 });
10543
10544  
10545
10546  /*
10547  * Based on:
10548  * Ext JS Library 1.1.1
10549  * Copyright(c) 2006-2007, Ext JS, LLC.
10550  *
10551  * Originally Released Under LGPL - original licence link has changed is not relivant.
10552  *
10553  * Fork - LGPL
10554  * <script type="text/javascript">
10555  */
10556
10557 // as we use this in bootstrap.
10558 Roo.namespace('Roo.form');
10559  /**
10560  * @class Roo.form.Action
10561  * Internal Class used to handle form actions
10562  * @constructor
10563  * @param {Roo.form.BasicForm} el The form element or its id
10564  * @param {Object} config Configuration options
10565  */
10566
10567  
10568  
10569 // define the action interface
10570 Roo.form.Action = function(form, options){
10571     this.form = form;
10572     this.options = options || {};
10573 };
10574 /**
10575  * Client Validation Failed
10576  * @const 
10577  */
10578 Roo.form.Action.CLIENT_INVALID = 'client';
10579 /**
10580  * Server Validation Failed
10581  * @const 
10582  */
10583 Roo.form.Action.SERVER_INVALID = 'server';
10584  /**
10585  * Connect to Server Failed
10586  * @const 
10587  */
10588 Roo.form.Action.CONNECT_FAILURE = 'connect';
10589 /**
10590  * Reading Data from Server Failed
10591  * @const 
10592  */
10593 Roo.form.Action.LOAD_FAILURE = 'load';
10594
10595 Roo.form.Action.prototype = {
10596     type : 'default',
10597     failureType : undefined,
10598     response : undefined,
10599     result : undefined,
10600
10601     // interface method
10602     run : function(options){
10603
10604     },
10605
10606     // interface method
10607     success : function(response){
10608
10609     },
10610
10611     // interface method
10612     handleResponse : function(response){
10613
10614     },
10615
10616     // default connection failure
10617     failure : function(response){
10618         
10619         this.response = response;
10620         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10621         this.form.afterAction(this, false);
10622     },
10623
10624     processResponse : function(response){
10625         this.response = response;
10626         if(!response.responseText){
10627             return true;
10628         }
10629         this.result = this.handleResponse(response);
10630         return this.result;
10631     },
10632
10633     // utility functions used internally
10634     getUrl : function(appendParams){
10635         var url = this.options.url || this.form.url || this.form.el.dom.action;
10636         if(appendParams){
10637             var p = this.getParams();
10638             if(p){
10639                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10640             }
10641         }
10642         return url;
10643     },
10644
10645     getMethod : function(){
10646         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10647     },
10648
10649     getParams : function(){
10650         var bp = this.form.baseParams;
10651         var p = this.options.params;
10652         if(p){
10653             if(typeof p == "object"){
10654                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10655             }else if(typeof p == 'string' && bp){
10656                 p += '&' + Roo.urlEncode(bp);
10657             }
10658         }else if(bp){
10659             p = Roo.urlEncode(bp);
10660         }
10661         return p;
10662     },
10663
10664     createCallback : function(){
10665         return {
10666             success: this.success,
10667             failure: this.failure,
10668             scope: this,
10669             timeout: (this.form.timeout*1000),
10670             upload: this.form.fileUpload ? this.success : undefined
10671         };
10672     }
10673 };
10674
10675 Roo.form.Action.Submit = function(form, options){
10676     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10677 };
10678
10679 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10680     type : 'submit',
10681
10682     haveProgress : false,
10683     uploadComplete : false,
10684     
10685     // uploadProgress indicator.
10686     uploadProgress : function()
10687     {
10688         if (!this.form.progressUrl) {
10689             return;
10690         }
10691         
10692         if (!this.haveProgress) {
10693             Roo.MessageBox.progress("Uploading", "Uploading");
10694         }
10695         if (this.uploadComplete) {
10696            Roo.MessageBox.hide();
10697            return;
10698         }
10699         
10700         this.haveProgress = true;
10701    
10702         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10703         
10704         var c = new Roo.data.Connection();
10705         c.request({
10706             url : this.form.progressUrl,
10707             params: {
10708                 id : uid
10709             },
10710             method: 'GET',
10711             success : function(req){
10712                //console.log(data);
10713                 var rdata = false;
10714                 var edata;
10715                 try  {
10716                    rdata = Roo.decode(req.responseText)
10717                 } catch (e) {
10718                     Roo.log("Invalid data from server..");
10719                     Roo.log(edata);
10720                     return;
10721                 }
10722                 if (!rdata || !rdata.success) {
10723                     Roo.log(rdata);
10724                     Roo.MessageBox.alert(Roo.encode(rdata));
10725                     return;
10726                 }
10727                 var data = rdata.data;
10728                 
10729                 if (this.uploadComplete) {
10730                    Roo.MessageBox.hide();
10731                    return;
10732                 }
10733                    
10734                 if (data){
10735                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10736                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10737                     );
10738                 }
10739                 this.uploadProgress.defer(2000,this);
10740             },
10741        
10742             failure: function(data) {
10743                 Roo.log('progress url failed ');
10744                 Roo.log(data);
10745             },
10746             scope : this
10747         });
10748            
10749     },
10750     
10751     
10752     run : function()
10753     {
10754         // run get Values on the form, so it syncs any secondary forms.
10755         this.form.getValues();
10756         
10757         var o = this.options;
10758         var method = this.getMethod();
10759         var isPost = method == 'POST';
10760         if(o.clientValidation === false || this.form.isValid()){
10761             
10762             if (this.form.progressUrl) {
10763                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10764                     (new Date() * 1) + '' + Math.random());
10765                     
10766             } 
10767             
10768             
10769             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10770                 form:this.form.el.dom,
10771                 url:this.getUrl(!isPost),
10772                 method: method,
10773                 params:isPost ? this.getParams() : null,
10774                 isUpload: this.form.fileUpload,
10775                 formData : this.form.formData
10776             }));
10777             
10778             this.uploadProgress();
10779
10780         }else if (o.clientValidation !== false){ // client validation failed
10781             this.failureType = Roo.form.Action.CLIENT_INVALID;
10782             this.form.afterAction(this, false);
10783         }
10784     },
10785
10786     success : function(response)
10787     {
10788         this.uploadComplete= true;
10789         if (this.haveProgress) {
10790             Roo.MessageBox.hide();
10791         }
10792         
10793         
10794         var result = this.processResponse(response);
10795         if(result === true || result.success){
10796             this.form.afterAction(this, true);
10797             return;
10798         }
10799         if(result.errors){
10800             this.form.markInvalid(result.errors);
10801             this.failureType = Roo.form.Action.SERVER_INVALID;
10802         }
10803         this.form.afterAction(this, false);
10804     },
10805     failure : function(response)
10806     {
10807         this.uploadComplete= true;
10808         if (this.haveProgress) {
10809             Roo.MessageBox.hide();
10810         }
10811         
10812         this.response = response;
10813         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10814         this.form.afterAction(this, false);
10815     },
10816     
10817     handleResponse : function(response){
10818         if(this.form.errorReader){
10819             var rs = this.form.errorReader.read(response);
10820             var errors = [];
10821             if(rs.records){
10822                 for(var i = 0, len = rs.records.length; i < len; i++) {
10823                     var r = rs.records[i];
10824                     errors[i] = r.data;
10825                 }
10826             }
10827             if(errors.length < 1){
10828                 errors = null;
10829             }
10830             return {
10831                 success : rs.success,
10832                 errors : errors
10833             };
10834         }
10835         var ret = false;
10836         try {
10837             ret = Roo.decode(response.responseText);
10838         } catch (e) {
10839             ret = {
10840                 success: false,
10841                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10842                 errors : []
10843             };
10844         }
10845         return ret;
10846         
10847     }
10848 });
10849
10850
10851 Roo.form.Action.Load = function(form, options){
10852     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10853     this.reader = this.form.reader;
10854 };
10855
10856 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10857     type : 'load',
10858
10859     run : function(){
10860         
10861         Roo.Ajax.request(Roo.apply(
10862                 this.createCallback(), {
10863                     method:this.getMethod(),
10864                     url:this.getUrl(false),
10865                     params:this.getParams()
10866         }));
10867     },
10868
10869     success : function(response){
10870         
10871         var result = this.processResponse(response);
10872         if(result === true || !result.success || !result.data){
10873             this.failureType = Roo.form.Action.LOAD_FAILURE;
10874             this.form.afterAction(this, false);
10875             return;
10876         }
10877         this.form.clearInvalid();
10878         this.form.setValues(result.data);
10879         this.form.afterAction(this, true);
10880     },
10881
10882     handleResponse : function(response){
10883         if(this.form.reader){
10884             var rs = this.form.reader.read(response);
10885             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10886             return {
10887                 success : rs.success,
10888                 data : data
10889             };
10890         }
10891         return Roo.decode(response.responseText);
10892     }
10893 });
10894
10895 Roo.form.Action.ACTION_TYPES = {
10896     'load' : Roo.form.Action.Load,
10897     'submit' : Roo.form.Action.Submit
10898 };/*
10899  * - LGPL
10900  *
10901  * form
10902  *
10903  */
10904
10905 /**
10906  * @class Roo.bootstrap.Form
10907  * @extends Roo.bootstrap.Component
10908  * Bootstrap Form class
10909  * @cfg {String} method  GET | POST (default POST)
10910  * @cfg {String} labelAlign top | left (default top)
10911  * @cfg {String} align left  | right - for navbars
10912  * @cfg {Boolean} loadMask load mask when submit (default true)
10913
10914  *
10915  * @constructor
10916  * Create a new Form
10917  * @param {Object} config The config object
10918  */
10919
10920
10921 Roo.bootstrap.Form = function(config){
10922     
10923     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10924     
10925     Roo.bootstrap.Form.popover.apply();
10926     
10927     this.addEvents({
10928         /**
10929          * @event clientvalidation
10930          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10931          * @param {Form} this
10932          * @param {Boolean} valid true if the form has passed client-side validation
10933          */
10934         clientvalidation: true,
10935         /**
10936          * @event beforeaction
10937          * Fires before any action is performed. Return false to cancel the action.
10938          * @param {Form} this
10939          * @param {Action} action The action to be performed
10940          */
10941         beforeaction: true,
10942         /**
10943          * @event actionfailed
10944          * Fires when an action fails.
10945          * @param {Form} this
10946          * @param {Action} action The action that failed
10947          */
10948         actionfailed : true,
10949         /**
10950          * @event actioncomplete
10951          * Fires when an action is completed.
10952          * @param {Form} this
10953          * @param {Action} action The action that completed
10954          */
10955         actioncomplete : true
10956     });
10957 };
10958
10959 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10960
10961      /**
10962      * @cfg {String} method
10963      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10964      */
10965     method : 'POST',
10966     /**
10967      * @cfg {String} url
10968      * The URL to use for form actions if one isn't supplied in the action options.
10969      */
10970     /**
10971      * @cfg {Boolean} fileUpload
10972      * Set to true if this form is a file upload.
10973      */
10974
10975     /**
10976      * @cfg {Object} baseParams
10977      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10978      */
10979
10980     /**
10981      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10982      */
10983     timeout: 30,
10984     /**
10985      * @cfg {Sting} align (left|right) for navbar forms
10986      */
10987     align : 'left',
10988
10989     // private
10990     activeAction : null,
10991
10992     /**
10993      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10994      * element by passing it or its id or mask the form itself by passing in true.
10995      * @type Mixed
10996      */
10997     waitMsgTarget : false,
10998
10999     loadMask : true,
11000     
11001     /**
11002      * @cfg {Boolean} errorMask (true|false) default false
11003      */
11004     errorMask : false,
11005     
11006     /**
11007      * @cfg {Number} maskOffset Default 100
11008      */
11009     maskOffset : 100,
11010     
11011     /**
11012      * @cfg {Boolean} maskBody
11013      */
11014     maskBody : false,
11015
11016     getAutoCreate : function(){
11017
11018         var cfg = {
11019             tag: 'form',
11020             method : this.method || 'POST',
11021             id : this.id || Roo.id(),
11022             cls : ''
11023         };
11024         if (this.parent().xtype.match(/^Nav/)) {
11025             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11026
11027         }
11028
11029         if (this.labelAlign == 'left' ) {
11030             cfg.cls += ' form-horizontal';
11031         }
11032
11033
11034         return cfg;
11035     },
11036     initEvents : function()
11037     {
11038         this.el.on('submit', this.onSubmit, this);
11039         // this was added as random key presses on the form where triggering form submit.
11040         this.el.on('keypress', function(e) {
11041             if (e.getCharCode() != 13) {
11042                 return true;
11043             }
11044             // we might need to allow it for textareas.. and some other items.
11045             // check e.getTarget().
11046
11047             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11048                 return true;
11049             }
11050
11051             Roo.log("keypress blocked");
11052
11053             e.preventDefault();
11054             return false;
11055         });
11056         
11057     },
11058     // private
11059     onSubmit : function(e){
11060         e.stopEvent();
11061     },
11062
11063      /**
11064      * Returns true if client-side validation on the form is successful.
11065      * @return Boolean
11066      */
11067     isValid : function(){
11068         var items = this.getItems();
11069         var valid = true;
11070         var target = false;
11071         
11072         items.each(function(f){
11073             
11074             if(f.validate()){
11075                 return;
11076             }
11077             
11078             Roo.log('invalid field: ' + f.name);
11079             
11080             valid = false;
11081
11082             if(!target && f.el.isVisible(true)){
11083                 target = f;
11084             }
11085            
11086         });
11087         
11088         if(this.errorMask && !valid){
11089             Roo.bootstrap.Form.popover.mask(this, target);
11090         }
11091         
11092         return valid;
11093     },
11094     
11095     /**
11096      * Returns true if any fields in this form have changed since their original load.
11097      * @return Boolean
11098      */
11099     isDirty : function(){
11100         var dirty = false;
11101         var items = this.getItems();
11102         items.each(function(f){
11103            if(f.isDirty()){
11104                dirty = true;
11105                return false;
11106            }
11107            return true;
11108         });
11109         return dirty;
11110     },
11111      /**
11112      * Performs a predefined action (submit or load) or custom actions you define on this form.
11113      * @param {String} actionName The name of the action type
11114      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11115      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11116      * accept other config options):
11117      * <pre>
11118 Property          Type             Description
11119 ----------------  ---------------  ----------------------------------------------------------------------------------
11120 url               String           The url for the action (defaults to the form's url)
11121 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11122 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11123 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11124                                    validate the form on the client (defaults to false)
11125      * </pre>
11126      * @return {BasicForm} this
11127      */
11128     doAction : function(action, options){
11129         if(typeof action == 'string'){
11130             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11131         }
11132         if(this.fireEvent('beforeaction', this, action) !== false){
11133             this.beforeAction(action);
11134             action.run.defer(100, action);
11135         }
11136         return this;
11137     },
11138
11139     // private
11140     beforeAction : function(action){
11141         var o = action.options;
11142         
11143         if(this.loadMask){
11144             
11145             if(this.maskBody){
11146                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11147             } else {
11148                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11149             }
11150         }
11151         // not really supported yet.. ??
11152
11153         //if(this.waitMsgTarget === true){
11154         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11155         //}else if(this.waitMsgTarget){
11156         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11157         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11158         //}else {
11159         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11160        // }
11161
11162     },
11163
11164     // private
11165     afterAction : function(action, success){
11166         this.activeAction = null;
11167         var o = action.options;
11168
11169         if(this.loadMask){
11170             
11171             if(this.maskBody){
11172                 Roo.get(document.body).unmask();
11173             } else {
11174                 this.el.unmask();
11175             }
11176         }
11177         
11178         //if(this.waitMsgTarget === true){
11179 //            this.el.unmask();
11180         //}else if(this.waitMsgTarget){
11181         //    this.waitMsgTarget.unmask();
11182         //}else{
11183         //    Roo.MessageBox.updateProgress(1);
11184         //    Roo.MessageBox.hide();
11185        // }
11186         //
11187         if(success){
11188             if(o.reset){
11189                 this.reset();
11190             }
11191             Roo.callback(o.success, o.scope, [this, action]);
11192             this.fireEvent('actioncomplete', this, action);
11193
11194         }else{
11195
11196             // failure condition..
11197             // we have a scenario where updates need confirming.
11198             // eg. if a locking scenario exists..
11199             // we look for { errors : { needs_confirm : true }} in the response.
11200             if (
11201                 (typeof(action.result) != 'undefined')  &&
11202                 (typeof(action.result.errors) != 'undefined')  &&
11203                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11204            ){
11205                 var _t = this;
11206                 Roo.log("not supported yet");
11207                  /*
11208
11209                 Roo.MessageBox.confirm(
11210                     "Change requires confirmation",
11211                     action.result.errorMsg,
11212                     function(r) {
11213                         if (r != 'yes') {
11214                             return;
11215                         }
11216                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11217                     }
11218
11219                 );
11220                 */
11221
11222
11223                 return;
11224             }
11225
11226             Roo.callback(o.failure, o.scope, [this, action]);
11227             // show an error message if no failed handler is set..
11228             if (!this.hasListener('actionfailed')) {
11229                 Roo.log("need to add dialog support");
11230                 /*
11231                 Roo.MessageBox.alert("Error",
11232                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11233                         action.result.errorMsg :
11234                         "Saving Failed, please check your entries or try again"
11235                 );
11236                 */
11237             }
11238
11239             this.fireEvent('actionfailed', this, action);
11240         }
11241
11242     },
11243     /**
11244      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11245      * @param {String} id The value to search for
11246      * @return Field
11247      */
11248     findField : function(id){
11249         var items = this.getItems();
11250         var field = items.get(id);
11251         if(!field){
11252              items.each(function(f){
11253                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11254                     field = f;
11255                     return false;
11256                 }
11257                 return true;
11258             });
11259         }
11260         return field || null;
11261     },
11262      /**
11263      * Mark fields in this form invalid in bulk.
11264      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11265      * @return {BasicForm} this
11266      */
11267     markInvalid : function(errors){
11268         if(errors instanceof Array){
11269             for(var i = 0, len = errors.length; i < len; i++){
11270                 var fieldError = errors[i];
11271                 var f = this.findField(fieldError.id);
11272                 if(f){
11273                     f.markInvalid(fieldError.msg);
11274                 }
11275             }
11276         }else{
11277             var field, id;
11278             for(id in errors){
11279                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11280                     field.markInvalid(errors[id]);
11281                 }
11282             }
11283         }
11284         //Roo.each(this.childForms || [], function (f) {
11285         //    f.markInvalid(errors);
11286         //});
11287
11288         return this;
11289     },
11290
11291     /**
11292      * Set values for fields in this form in bulk.
11293      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11294      * @return {BasicForm} this
11295      */
11296     setValues : function(values){
11297         if(values instanceof Array){ // array of objects
11298             for(var i = 0, len = values.length; i < len; i++){
11299                 var v = values[i];
11300                 var f = this.findField(v.id);
11301                 if(f){
11302                     f.setValue(v.value);
11303                     if(this.trackResetOnLoad){
11304                         f.originalValue = f.getValue();
11305                     }
11306                 }
11307             }
11308         }else{ // object hash
11309             var field, id;
11310             for(id in values){
11311                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11312
11313                     if (field.setFromData &&
11314                         field.valueField &&
11315                         field.displayField &&
11316                         // combos' with local stores can
11317                         // be queried via setValue()
11318                         // to set their value..
11319                         (field.store && !field.store.isLocal)
11320                         ) {
11321                         // it's a combo
11322                         var sd = { };
11323                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11324                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11325                         field.setFromData(sd);
11326
11327                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11328                         
11329                         field.setFromData(values);
11330                         
11331                     } else {
11332                         field.setValue(values[id]);
11333                     }
11334
11335
11336                     if(this.trackResetOnLoad){
11337                         field.originalValue = field.getValue();
11338                     }
11339                 }
11340             }
11341         }
11342
11343         //Roo.each(this.childForms || [], function (f) {
11344         //    f.setValues(values);
11345         //});
11346
11347         return this;
11348     },
11349
11350     /**
11351      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11352      * they are returned as an array.
11353      * @param {Boolean} asString
11354      * @return {Object}
11355      */
11356     getValues : function(asString){
11357         //if (this.childForms) {
11358             // copy values from the child forms
11359         //    Roo.each(this.childForms, function (f) {
11360         //        this.setValues(f.getValues());
11361         //    }, this);
11362         //}
11363
11364
11365
11366         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11367         if(asString === true){
11368             return fs;
11369         }
11370         return Roo.urlDecode(fs);
11371     },
11372
11373     /**
11374      * Returns the fields in this form as an object with key/value pairs.
11375      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11376      * @return {Object}
11377      */
11378     getFieldValues : function(with_hidden)
11379     {
11380         var items = this.getItems();
11381         var ret = {};
11382         items.each(function(f){
11383             
11384             if (!f.getName()) {
11385                 return;
11386             }
11387             
11388             var v = f.getValue();
11389             
11390             if (f.inputType =='radio') {
11391                 if (typeof(ret[f.getName()]) == 'undefined') {
11392                     ret[f.getName()] = ''; // empty..
11393                 }
11394
11395                 if (!f.el.dom.checked) {
11396                     return;
11397
11398                 }
11399                 v = f.el.dom.value;
11400
11401             }
11402             
11403             if(f.xtype == 'MoneyField'){
11404                 ret[f.currencyName] = f.getCurrency();
11405             }
11406
11407             // not sure if this supported any more..
11408             if ((typeof(v) == 'object') && f.getRawValue) {
11409                 v = f.getRawValue() ; // dates..
11410             }
11411             // combo boxes where name != hiddenName...
11412             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11413                 ret[f.name] = f.getRawValue();
11414             }
11415             ret[f.getName()] = v;
11416         });
11417
11418         return ret;
11419     },
11420
11421     /**
11422      * Clears all invalid messages in this form.
11423      * @return {BasicForm} this
11424      */
11425     clearInvalid : function(){
11426         var items = this.getItems();
11427
11428         items.each(function(f){
11429            f.clearInvalid();
11430         });
11431
11432         return this;
11433     },
11434
11435     /**
11436      * Resets this form.
11437      * @return {BasicForm} this
11438      */
11439     reset : function(){
11440         var items = this.getItems();
11441         items.each(function(f){
11442             f.reset();
11443         });
11444
11445         Roo.each(this.childForms || [], function (f) {
11446             f.reset();
11447         });
11448
11449
11450         return this;
11451     },
11452     
11453     getItems : function()
11454     {
11455         var r=new Roo.util.MixedCollection(false, function(o){
11456             return o.id || (o.id = Roo.id());
11457         });
11458         var iter = function(el) {
11459             if (el.inputEl) {
11460                 r.add(el);
11461             }
11462             if (!el.items) {
11463                 return;
11464             }
11465             Roo.each(el.items,function(e) {
11466                 iter(e);
11467             });
11468         };
11469
11470         iter(this);
11471         return r;
11472     },
11473     
11474     hideFields : function(items)
11475     {
11476         Roo.each(items, function(i){
11477             
11478             var f = this.findField(i);
11479             
11480             if(!f){
11481                 return;
11482             }
11483             
11484             f.hide();
11485             
11486         }, this);
11487     },
11488     
11489     showFields : function(items)
11490     {
11491         Roo.each(items, function(i){
11492             
11493             var f = this.findField(i);
11494             
11495             if(!f){
11496                 return;
11497             }
11498             
11499             f.show();
11500             
11501         }, this);
11502     }
11503
11504 });
11505
11506 Roo.apply(Roo.bootstrap.Form, {
11507     
11508     popover : {
11509         
11510         padding : 5,
11511         
11512         isApplied : false,
11513         
11514         isMasked : false,
11515         
11516         form : false,
11517         
11518         target : false,
11519         
11520         toolTip : false,
11521         
11522         intervalID : false,
11523         
11524         maskEl : false,
11525         
11526         apply : function()
11527         {
11528             if(this.isApplied){
11529                 return;
11530             }
11531             
11532             this.maskEl = {
11533                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11534                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11535                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11536                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11537             };
11538             
11539             this.maskEl.top.enableDisplayMode("block");
11540             this.maskEl.left.enableDisplayMode("block");
11541             this.maskEl.bottom.enableDisplayMode("block");
11542             this.maskEl.right.enableDisplayMode("block");
11543             
11544             this.toolTip = new Roo.bootstrap.Tooltip({
11545                 cls : 'roo-form-error-popover',
11546                 alignment : {
11547                     'left' : ['r-l', [-2,0], 'right'],
11548                     'right' : ['l-r', [2,0], 'left'],
11549                     'bottom' : ['tl-bl', [0,2], 'top'],
11550                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11551                 }
11552             });
11553             
11554             this.toolTip.render(Roo.get(document.body));
11555
11556             this.toolTip.el.enableDisplayMode("block");
11557             
11558             Roo.get(document.body).on('click', function(){
11559                 this.unmask();
11560             }, this);
11561             
11562             Roo.get(document.body).on('touchstart', function(){
11563                 this.unmask();
11564             }, this);
11565             
11566             this.isApplied = true
11567         },
11568         
11569         mask : function(form, target)
11570         {
11571             this.form = form;
11572             
11573             this.target = target;
11574             
11575             if(!this.form.errorMask || !target.el){
11576                 return;
11577             }
11578             
11579             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11580             
11581             Roo.log(scrollable);
11582             
11583             var ot = this.target.el.calcOffsetsTo(scrollable);
11584             
11585             var scrollTo = ot[1] - this.form.maskOffset;
11586             
11587             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11588             
11589             scrollable.scrollTo('top', scrollTo);
11590             
11591             var box = this.target.el.getBox();
11592             Roo.log(box);
11593             var zIndex = Roo.bootstrap.Modal.zIndex++;
11594
11595             
11596             this.maskEl.top.setStyle('position', 'absolute');
11597             this.maskEl.top.setStyle('z-index', zIndex);
11598             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11599             this.maskEl.top.setLeft(0);
11600             this.maskEl.top.setTop(0);
11601             this.maskEl.top.show();
11602             
11603             this.maskEl.left.setStyle('position', 'absolute');
11604             this.maskEl.left.setStyle('z-index', zIndex);
11605             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11606             this.maskEl.left.setLeft(0);
11607             this.maskEl.left.setTop(box.y - this.padding);
11608             this.maskEl.left.show();
11609
11610             this.maskEl.bottom.setStyle('position', 'absolute');
11611             this.maskEl.bottom.setStyle('z-index', zIndex);
11612             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11613             this.maskEl.bottom.setLeft(0);
11614             this.maskEl.bottom.setTop(box.bottom + this.padding);
11615             this.maskEl.bottom.show();
11616
11617             this.maskEl.right.setStyle('position', 'absolute');
11618             this.maskEl.right.setStyle('z-index', zIndex);
11619             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11620             this.maskEl.right.setLeft(box.right + this.padding);
11621             this.maskEl.right.setTop(box.y - this.padding);
11622             this.maskEl.right.show();
11623
11624             this.toolTip.bindEl = this.target.el;
11625
11626             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11627
11628             var tip = this.target.blankText;
11629
11630             if(this.target.getValue() !== '' ) {
11631                 
11632                 if (this.target.invalidText.length) {
11633                     tip = this.target.invalidText;
11634                 } else if (this.target.regexText.length){
11635                     tip = this.target.regexText;
11636                 }
11637             }
11638
11639             this.toolTip.show(tip);
11640
11641             this.intervalID = window.setInterval(function() {
11642                 Roo.bootstrap.Form.popover.unmask();
11643             }, 10000);
11644
11645             window.onwheel = function(){ return false;};
11646             
11647             (function(){ this.isMasked = true; }).defer(500, this);
11648             
11649         },
11650         
11651         unmask : function()
11652         {
11653             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11654                 return;
11655             }
11656             
11657             this.maskEl.top.setStyle('position', 'absolute');
11658             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11659             this.maskEl.top.hide();
11660
11661             this.maskEl.left.setStyle('position', 'absolute');
11662             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11663             this.maskEl.left.hide();
11664
11665             this.maskEl.bottom.setStyle('position', 'absolute');
11666             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11667             this.maskEl.bottom.hide();
11668
11669             this.maskEl.right.setStyle('position', 'absolute');
11670             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11671             this.maskEl.right.hide();
11672             
11673             this.toolTip.hide();
11674             
11675             this.toolTip.el.hide();
11676             
11677             window.onwheel = function(){ return true;};
11678             
11679             if(this.intervalID){
11680                 window.clearInterval(this.intervalID);
11681                 this.intervalID = false;
11682             }
11683             
11684             this.isMasked = false;
11685             
11686         }
11687         
11688     }
11689     
11690 });
11691
11692 /*
11693  * Based on:
11694  * Ext JS Library 1.1.1
11695  * Copyright(c) 2006-2007, Ext JS, LLC.
11696  *
11697  * Originally Released Under LGPL - original licence link has changed is not relivant.
11698  *
11699  * Fork - LGPL
11700  * <script type="text/javascript">
11701  */
11702 /**
11703  * @class Roo.form.VTypes
11704  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11705  * @singleton
11706  */
11707 Roo.form.VTypes = function(){
11708     // closure these in so they are only created once.
11709     var alpha = /^[a-zA-Z_]+$/;
11710     var alphanum = /^[a-zA-Z0-9_]+$/;
11711     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11712     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11713
11714     // All these messages and functions are configurable
11715     return {
11716         /**
11717          * The function used to validate email addresses
11718          * @param {String} value The email address
11719          */
11720         'email' : function(v){
11721             return email.test(v);
11722         },
11723         /**
11724          * The error text to display when the email validation function returns false
11725          * @type String
11726          */
11727         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11728         /**
11729          * The keystroke filter mask to be applied on email input
11730          * @type RegExp
11731          */
11732         'emailMask' : /[a-z0-9_\.\-@]/i,
11733
11734         /**
11735          * The function used to validate URLs
11736          * @param {String} value The URL
11737          */
11738         'url' : function(v){
11739             return url.test(v);
11740         },
11741         /**
11742          * The error text to display when the url validation function returns false
11743          * @type String
11744          */
11745         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11746         
11747         /**
11748          * The function used to validate alpha values
11749          * @param {String} value The value
11750          */
11751         'alpha' : function(v){
11752             return alpha.test(v);
11753         },
11754         /**
11755          * The error text to display when the alpha validation function returns false
11756          * @type String
11757          */
11758         'alphaText' : 'This field should only contain letters and _',
11759         /**
11760          * The keystroke filter mask to be applied on alpha input
11761          * @type RegExp
11762          */
11763         'alphaMask' : /[a-z_]/i,
11764
11765         /**
11766          * The function used to validate alphanumeric values
11767          * @param {String} value The value
11768          */
11769         'alphanum' : function(v){
11770             return alphanum.test(v);
11771         },
11772         /**
11773          * The error text to display when the alphanumeric validation function returns false
11774          * @type String
11775          */
11776         'alphanumText' : 'This field should only contain letters, numbers and _',
11777         /**
11778          * The keystroke filter mask to be applied on alphanumeric input
11779          * @type RegExp
11780          */
11781         'alphanumMask' : /[a-z0-9_]/i
11782     };
11783 }();/*
11784  * - LGPL
11785  *
11786  * Input
11787  * 
11788  */
11789
11790 /**
11791  * @class Roo.bootstrap.Input
11792  * @extends Roo.bootstrap.Component
11793  * Bootstrap Input class
11794  * @cfg {Boolean} disabled is it disabled
11795  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11796  * @cfg {String} name name of the input
11797  * @cfg {string} fieldLabel - the label associated
11798  * @cfg {string} placeholder - placeholder to put in text.
11799  * @cfg {string}  before - input group add on before
11800  * @cfg {string} after - input group add on after
11801  * @cfg {string} size - (lg|sm) or leave empty..
11802  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11803  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11804  * @cfg {Number} md colspan out of 12 for computer-sized screens
11805  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11806  * @cfg {string} value default value of the input
11807  * @cfg {Number} labelWidth set the width of label 
11808  * @cfg {Number} labellg set the width of label (1-12)
11809  * @cfg {Number} labelmd set the width of label (1-12)
11810  * @cfg {Number} labelsm set the width of label (1-12)
11811  * @cfg {Number} labelxs set the width of label (1-12)
11812  * @cfg {String} labelAlign (top|left)
11813  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11814  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11815  * @cfg {String} indicatorpos (left|right) default left
11816  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11817  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11818  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11819
11820  * @cfg {String} align (left|center|right) Default left
11821  * @cfg {Boolean} forceFeedback (true|false) Default false
11822  * 
11823  * @constructor
11824  * Create a new Input
11825  * @param {Object} config The config object
11826  */
11827
11828 Roo.bootstrap.Input = function(config){
11829     
11830     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11831     
11832     this.addEvents({
11833         /**
11834          * @event focus
11835          * Fires when this field receives input focus.
11836          * @param {Roo.form.Field} this
11837          */
11838         focus : true,
11839         /**
11840          * @event blur
11841          * Fires when this field loses input focus.
11842          * @param {Roo.form.Field} this
11843          */
11844         blur : true,
11845         /**
11846          * @event specialkey
11847          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11848          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11849          * @param {Roo.form.Field} this
11850          * @param {Roo.EventObject} e The event object
11851          */
11852         specialkey : true,
11853         /**
11854          * @event change
11855          * Fires just before the field blurs if the field value has changed.
11856          * @param {Roo.form.Field} this
11857          * @param {Mixed} newValue The new value
11858          * @param {Mixed} oldValue The original value
11859          */
11860         change : true,
11861         /**
11862          * @event invalid
11863          * Fires after the field has been marked as invalid.
11864          * @param {Roo.form.Field} this
11865          * @param {String} msg The validation message
11866          */
11867         invalid : true,
11868         /**
11869          * @event valid
11870          * Fires after the field has been validated with no errors.
11871          * @param {Roo.form.Field} this
11872          */
11873         valid : true,
11874          /**
11875          * @event keyup
11876          * Fires after the key up
11877          * @param {Roo.form.Field} this
11878          * @param {Roo.EventObject}  e The event Object
11879          */
11880         keyup : true,
11881         /**
11882          * @event paste
11883          * Fires after the user pastes into input
11884          * @param {Roo.form.Field} this
11885          * @param {Roo.EventObject}  e The event Object
11886          */
11887         paste : true
11888     });
11889 };
11890
11891 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11892      /**
11893      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11894       automatic validation (defaults to "keyup").
11895      */
11896     validationEvent : "keyup",
11897      /**
11898      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11899      */
11900     validateOnBlur : true,
11901     /**
11902      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11903      */
11904     validationDelay : 250,
11905      /**
11906      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11907      */
11908     focusClass : "x-form-focus",  // not needed???
11909     
11910        
11911     /**
11912      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11913      */
11914     invalidClass : "has-warning",
11915     
11916     /**
11917      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11918      */
11919     validClass : "has-success",
11920     
11921     /**
11922      * @cfg {Boolean} hasFeedback (true|false) default true
11923      */
11924     hasFeedback : true,
11925     
11926     /**
11927      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11928      */
11929     invalidFeedbackClass : "glyphicon-warning-sign",
11930     
11931     /**
11932      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11933      */
11934     validFeedbackClass : "glyphicon-ok",
11935     
11936     /**
11937      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11938      */
11939     selectOnFocus : false,
11940     
11941      /**
11942      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11943      */
11944     maskRe : null,
11945        /**
11946      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11947      */
11948     vtype : null,
11949     
11950       /**
11951      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11952      */
11953     disableKeyFilter : false,
11954     
11955        /**
11956      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11957      */
11958     disabled : false,
11959      /**
11960      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11961      */
11962     allowBlank : true,
11963     /**
11964      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11965      */
11966     blankText : "Please complete this mandatory field",
11967     
11968      /**
11969      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11970      */
11971     minLength : 0,
11972     /**
11973      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11974      */
11975     maxLength : Number.MAX_VALUE,
11976     /**
11977      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11978      */
11979     minLengthText : "The minimum length for this field is {0}",
11980     /**
11981      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11982      */
11983     maxLengthText : "The maximum length for this field is {0}",
11984   
11985     
11986     /**
11987      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11988      * If available, this function will be called only after the basic validators all return true, and will be passed the
11989      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11990      */
11991     validator : null,
11992     /**
11993      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11994      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11995      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11996      */
11997     regex : null,
11998     /**
11999      * @cfg {String} regexText -- Depricated - use Invalid Text
12000      */
12001     regexText : "",
12002     
12003     /**
12004      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12005      */
12006     invalidText : "",
12007     
12008     
12009     
12010     autocomplete: false,
12011     
12012     
12013     fieldLabel : '',
12014     inputType : 'text',
12015     
12016     name : false,
12017     placeholder: false,
12018     before : false,
12019     after : false,
12020     size : false,
12021     hasFocus : false,
12022     preventMark: false,
12023     isFormField : true,
12024     value : '',
12025     labelWidth : 2,
12026     labelAlign : false,
12027     readOnly : false,
12028     align : false,
12029     formatedValue : false,
12030     forceFeedback : false,
12031     
12032     indicatorpos : 'left',
12033     
12034     labellg : 0,
12035     labelmd : 0,
12036     labelsm : 0,
12037     labelxs : 0,
12038     
12039     capture : '',
12040     accept : '',
12041     
12042     parentLabelAlign : function()
12043     {
12044         var parent = this;
12045         while (parent.parent()) {
12046             parent = parent.parent();
12047             if (typeof(parent.labelAlign) !='undefined') {
12048                 return parent.labelAlign;
12049             }
12050         }
12051         return 'left';
12052         
12053     },
12054     
12055     getAutoCreate : function()
12056     {
12057         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12058         
12059         var id = Roo.id();
12060         
12061         var cfg = {};
12062         
12063         if(this.inputType != 'hidden'){
12064             cfg.cls = 'form-group' //input-group
12065         }
12066         
12067         var input =  {
12068             tag: 'input',
12069             id : id,
12070             type : this.inputType,
12071             value : this.value,
12072             cls : 'form-control',
12073             placeholder : this.placeholder || '',
12074             autocomplete : this.autocomplete || 'new-password'
12075         };
12076         if (this.inputType == 'file') {
12077             input.style = 'overflow:hidden'; // why not in CSS?
12078         }
12079         
12080         if(this.capture.length){
12081             input.capture = this.capture;
12082         }
12083         
12084         if(this.accept.length){
12085             input.accept = this.accept + "/*";
12086         }
12087         
12088         if(this.align){
12089             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12090         }
12091         
12092         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12093             input.maxLength = this.maxLength;
12094         }
12095         
12096         if (this.disabled) {
12097             input.disabled=true;
12098         }
12099         
12100         if (this.readOnly) {
12101             input.readonly=true;
12102         }
12103         
12104         if (this.name) {
12105             input.name = this.name;
12106         }
12107         
12108         if (this.size) {
12109             input.cls += ' input-' + this.size;
12110         }
12111         
12112         var settings=this;
12113         ['xs','sm','md','lg'].map(function(size){
12114             if (settings[size]) {
12115                 cfg.cls += ' col-' + size + '-' + settings[size];
12116             }
12117         });
12118         
12119         var inputblock = input;
12120         
12121         var feedback = {
12122             tag: 'span',
12123             cls: 'glyphicon form-control-feedback'
12124         };
12125             
12126         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12127             
12128             inputblock = {
12129                 cls : 'has-feedback',
12130                 cn :  [
12131                     input,
12132                     feedback
12133                 ] 
12134             };  
12135         }
12136         
12137         if (this.before || this.after) {
12138             
12139             inputblock = {
12140                 cls : 'input-group',
12141                 cn :  [] 
12142             };
12143             
12144             if (this.before && typeof(this.before) == 'string') {
12145                 
12146                 inputblock.cn.push({
12147                     tag :'span',
12148                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12149                     html : this.before
12150                 });
12151             }
12152             if (this.before && typeof(this.before) == 'object') {
12153                 this.before = Roo.factory(this.before);
12154                 
12155                 inputblock.cn.push({
12156                     tag :'span',
12157                     cls : 'roo-input-before input-group-prepend   input-group-' +
12158                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12159                 });
12160             }
12161             
12162             inputblock.cn.push(input);
12163             
12164             if (this.after && typeof(this.after) == 'string') {
12165                 inputblock.cn.push({
12166                     tag :'span',
12167                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12168                     html : this.after
12169                 });
12170             }
12171             if (this.after && typeof(this.after) == 'object') {
12172                 this.after = Roo.factory(this.after);
12173                 
12174                 inputblock.cn.push({
12175                     tag :'span',
12176                     cls : 'roo-input-after input-group-append  input-group-' +
12177                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12178                 });
12179             }
12180             
12181             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12182                 inputblock.cls += ' has-feedback';
12183                 inputblock.cn.push(feedback);
12184             }
12185         };
12186         var indicator = {
12187             tag : 'i',
12188             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12189             tooltip : 'This field is required'
12190         };
12191         if (this.allowBlank ) {
12192             indicator.style = this.allowBlank ? ' display:none' : '';
12193         }
12194         if (align ==='left' && this.fieldLabel.length) {
12195             
12196             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12197             
12198             cfg.cn = [
12199                 indicator,
12200                 {
12201                     tag: 'label',
12202                     'for' :  id,
12203                     cls : 'control-label col-form-label',
12204                     html : this.fieldLabel
12205
12206                 },
12207                 {
12208                     cls : "", 
12209                     cn: [
12210                         inputblock
12211                     ]
12212                 }
12213             ];
12214             
12215             var labelCfg = cfg.cn[1];
12216             var contentCfg = cfg.cn[2];
12217             
12218             if(this.indicatorpos == 'right'){
12219                 cfg.cn = [
12220                     {
12221                         tag: 'label',
12222                         'for' :  id,
12223                         cls : 'control-label col-form-label',
12224                         cn : [
12225                             {
12226                                 tag : 'span',
12227                                 html : this.fieldLabel
12228                             },
12229                             indicator
12230                         ]
12231                     },
12232                     {
12233                         cls : "",
12234                         cn: [
12235                             inputblock
12236                         ]
12237                     }
12238
12239                 ];
12240                 
12241                 labelCfg = cfg.cn[0];
12242                 contentCfg = cfg.cn[1];
12243             
12244             }
12245             
12246             if(this.labelWidth > 12){
12247                 labelCfg.style = "width: " + this.labelWidth + 'px';
12248             }
12249             
12250             if(this.labelWidth < 13 && this.labelmd == 0){
12251                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12252             }
12253             
12254             if(this.labellg > 0){
12255                 labelCfg.cls += ' col-lg-' + this.labellg;
12256                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12257             }
12258             
12259             if(this.labelmd > 0){
12260                 labelCfg.cls += ' col-md-' + this.labelmd;
12261                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12262             }
12263             
12264             if(this.labelsm > 0){
12265                 labelCfg.cls += ' col-sm-' + this.labelsm;
12266                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12267             }
12268             
12269             if(this.labelxs > 0){
12270                 labelCfg.cls += ' col-xs-' + this.labelxs;
12271                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12272             }
12273             
12274             
12275         } else if ( this.fieldLabel.length) {
12276                 
12277             
12278             
12279             cfg.cn = [
12280                 {
12281                     tag : 'i',
12282                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12283                     tooltip : 'This field is required',
12284                     style : this.allowBlank ? ' display:none' : '' 
12285                 },
12286                 {
12287                     tag: 'label',
12288                    //cls : 'input-group-addon',
12289                     html : this.fieldLabel
12290
12291                 },
12292
12293                inputblock
12294
12295            ];
12296            
12297            if(this.indicatorpos == 'right'){
12298        
12299                 cfg.cn = [
12300                     {
12301                         tag: 'label',
12302                        //cls : 'input-group-addon',
12303                         html : this.fieldLabel
12304
12305                     },
12306                     {
12307                         tag : 'i',
12308                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12309                         tooltip : 'This field is required',
12310                         style : this.allowBlank ? ' display:none' : '' 
12311                     },
12312
12313                    inputblock
12314
12315                ];
12316
12317             }
12318
12319         } else {
12320             
12321             cfg.cn = [
12322
12323                     inputblock
12324
12325             ];
12326                 
12327                 
12328         };
12329         
12330         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12331            cfg.cls += ' navbar-form';
12332         }
12333         
12334         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12335             // on BS4 we do this only if not form 
12336             cfg.cls += ' navbar-form';
12337             cfg.tag = 'li';
12338         }
12339         
12340         return cfg;
12341         
12342     },
12343     /**
12344      * return the real input element.
12345      */
12346     inputEl: function ()
12347     {
12348         return this.el.select('input.form-control',true).first();
12349     },
12350     
12351     tooltipEl : function()
12352     {
12353         return this.inputEl();
12354     },
12355     
12356     indicatorEl : function()
12357     {
12358         if (Roo.bootstrap.version == 4) {
12359             return false; // not enabled in v4 yet.
12360         }
12361         
12362         var indicator = this.el.select('i.roo-required-indicator',true).first();
12363         
12364         if(!indicator){
12365             return false;
12366         }
12367         
12368         return indicator;
12369         
12370     },
12371     
12372     setDisabled : function(v)
12373     {
12374         var i  = this.inputEl().dom;
12375         if (!v) {
12376             i.removeAttribute('disabled');
12377             return;
12378             
12379         }
12380         i.setAttribute('disabled','true');
12381     },
12382     initEvents : function()
12383     {
12384           
12385         this.inputEl().on("keydown" , this.fireKey,  this);
12386         this.inputEl().on("focus", this.onFocus,  this);
12387         this.inputEl().on("blur", this.onBlur,  this);
12388         
12389         this.inputEl().relayEvent('keyup', this);
12390         this.inputEl().relayEvent('paste', this);
12391         
12392         this.indicator = this.indicatorEl();
12393         
12394         if(this.indicator){
12395             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12396         }
12397  
12398         // reference to original value for reset
12399         this.originalValue = this.getValue();
12400         //Roo.form.TextField.superclass.initEvents.call(this);
12401         if(this.validationEvent == 'keyup'){
12402             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12403             this.inputEl().on('keyup', this.filterValidation, this);
12404         }
12405         else if(this.validationEvent !== false){
12406             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12407         }
12408         
12409         if(this.selectOnFocus){
12410             this.on("focus", this.preFocus, this);
12411             
12412         }
12413         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12414             this.inputEl().on("keypress", this.filterKeys, this);
12415         } else {
12416             this.inputEl().relayEvent('keypress', this);
12417         }
12418        /* if(this.grow){
12419             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12420             this.el.on("click", this.autoSize,  this);
12421         }
12422         */
12423         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12424             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12425         }
12426         
12427         if (typeof(this.before) == 'object') {
12428             this.before.render(this.el.select('.roo-input-before',true).first());
12429         }
12430         if (typeof(this.after) == 'object') {
12431             this.after.render(this.el.select('.roo-input-after',true).first());
12432         }
12433         
12434         this.inputEl().on('change', this.onChange, this);
12435         
12436     },
12437     filterValidation : function(e){
12438         if(!e.isNavKeyPress()){
12439             this.validationTask.delay(this.validationDelay);
12440         }
12441     },
12442      /**
12443      * Validates the field value
12444      * @return {Boolean} True if the value is valid, else false
12445      */
12446     validate : function(){
12447         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12448         if(this.disabled || this.validateValue(this.getRawValue())){
12449             this.markValid();
12450             return true;
12451         }
12452         
12453         this.markInvalid();
12454         return false;
12455     },
12456     
12457     
12458     /**
12459      * Validates a value according to the field's validation rules and marks the field as invalid
12460      * if the validation fails
12461      * @param {Mixed} value The value to validate
12462      * @return {Boolean} True if the value is valid, else false
12463      */
12464     validateValue : function(value)
12465     {
12466         if(this.getVisibilityEl().hasClass('hidden')){
12467             return true;
12468         }
12469         
12470         if(value.length < 1)  { // if it's blank
12471             if(this.allowBlank){
12472                 return true;
12473             }
12474             return false;
12475         }
12476         
12477         if(value.length < this.minLength){
12478             return false;
12479         }
12480         if(value.length > this.maxLength){
12481             return false;
12482         }
12483         if(this.vtype){
12484             var vt = Roo.form.VTypes;
12485             if(!vt[this.vtype](value, this)){
12486                 return false;
12487             }
12488         }
12489         if(typeof this.validator == "function"){
12490             var msg = this.validator(value);
12491             if(msg !== true){
12492                 return false;
12493             }
12494             if (typeof(msg) == 'string') {
12495                 this.invalidText = msg;
12496             }
12497         }
12498         
12499         if(this.regex && !this.regex.test(value)){
12500             return false;
12501         }
12502         
12503         return true;
12504     },
12505     
12506      // private
12507     fireKey : function(e){
12508         //Roo.log('field ' + e.getKey());
12509         if(e.isNavKeyPress()){
12510             this.fireEvent("specialkey", this, e);
12511         }
12512     },
12513     focus : function (selectText){
12514         if(this.rendered){
12515             this.inputEl().focus();
12516             if(selectText === true){
12517                 this.inputEl().dom.select();
12518             }
12519         }
12520         return this;
12521     } ,
12522     
12523     onFocus : function(){
12524         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12525            // this.el.addClass(this.focusClass);
12526         }
12527         if(!this.hasFocus){
12528             this.hasFocus = true;
12529             this.startValue = this.getValue();
12530             this.fireEvent("focus", this);
12531         }
12532     },
12533     
12534     beforeBlur : Roo.emptyFn,
12535
12536     
12537     // private
12538     onBlur : function(){
12539         this.beforeBlur();
12540         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12541             //this.el.removeClass(this.focusClass);
12542         }
12543         this.hasFocus = false;
12544         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12545             this.validate();
12546         }
12547         var v = this.getValue();
12548         if(String(v) !== String(this.startValue)){
12549             this.fireEvent('change', this, v, this.startValue);
12550         }
12551         this.fireEvent("blur", this);
12552     },
12553     
12554     onChange : function(e)
12555     {
12556         var v = this.getValue();
12557         if(String(v) !== String(this.startValue)){
12558             this.fireEvent('change', this, v, this.startValue);
12559         }
12560         
12561     },
12562     
12563     /**
12564      * Resets the current field value to the originally loaded value and clears any validation messages
12565      */
12566     reset : function(){
12567         this.setValue(this.originalValue);
12568         this.validate();
12569     },
12570      /**
12571      * Returns the name of the field
12572      * @return {Mixed} name The name field
12573      */
12574     getName: function(){
12575         return this.name;
12576     },
12577      /**
12578      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12579      * @return {Mixed} value The field value
12580      */
12581     getValue : function(){
12582         
12583         var v = this.inputEl().getValue();
12584         
12585         return v;
12586     },
12587     /**
12588      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12589      * @return {Mixed} value The field value
12590      */
12591     getRawValue : function(){
12592         var v = this.inputEl().getValue();
12593         
12594         return v;
12595     },
12596     
12597     /**
12598      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12599      * @param {Mixed} value The value to set
12600      */
12601     setRawValue : function(v){
12602         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12603     },
12604     
12605     selectText : function(start, end){
12606         var v = this.getRawValue();
12607         if(v.length > 0){
12608             start = start === undefined ? 0 : start;
12609             end = end === undefined ? v.length : end;
12610             var d = this.inputEl().dom;
12611             if(d.setSelectionRange){
12612                 d.setSelectionRange(start, end);
12613             }else if(d.createTextRange){
12614                 var range = d.createTextRange();
12615                 range.moveStart("character", start);
12616                 range.moveEnd("character", v.length-end);
12617                 range.select();
12618             }
12619         }
12620     },
12621     
12622     /**
12623      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12624      * @param {Mixed} value The value to set
12625      */
12626     setValue : function(v){
12627         this.value = v;
12628         if(this.rendered){
12629             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12630             this.validate();
12631         }
12632     },
12633     
12634     /*
12635     processValue : function(value){
12636         if(this.stripCharsRe){
12637             var newValue = value.replace(this.stripCharsRe, '');
12638             if(newValue !== value){
12639                 this.setRawValue(newValue);
12640                 return newValue;
12641             }
12642         }
12643         return value;
12644     },
12645   */
12646     preFocus : function(){
12647         
12648         if(this.selectOnFocus){
12649             this.inputEl().dom.select();
12650         }
12651     },
12652     filterKeys : function(e){
12653         var k = e.getKey();
12654         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12655             return;
12656         }
12657         var c = e.getCharCode(), cc = String.fromCharCode(c);
12658         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12659             return;
12660         }
12661         if(!this.maskRe.test(cc)){
12662             e.stopEvent();
12663         }
12664     },
12665      /**
12666      * Clear any invalid styles/messages for this field
12667      */
12668     clearInvalid : function(){
12669         
12670         if(!this.el || this.preventMark){ // not rendered
12671             return;
12672         }
12673         
12674         
12675         this.el.removeClass([this.invalidClass, 'is-invalid']);
12676         
12677         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12678             
12679             var feedback = this.el.select('.form-control-feedback', true).first();
12680             
12681             if(feedback){
12682                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12683             }
12684             
12685         }
12686         
12687         if(this.indicator){
12688             this.indicator.removeClass('visible');
12689             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12690         }
12691         
12692         this.fireEvent('valid', this);
12693     },
12694     
12695      /**
12696      * Mark this field as valid
12697      */
12698     markValid : function()
12699     {
12700         if(!this.el  || this.preventMark){ // not rendered...
12701             return;
12702         }
12703         
12704         this.el.removeClass([this.invalidClass, this.validClass]);
12705         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12706
12707         var feedback = this.el.select('.form-control-feedback', true).first();
12708             
12709         if(feedback){
12710             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12711         }
12712         
12713         if(this.indicator){
12714             this.indicator.removeClass('visible');
12715             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12716         }
12717         
12718         if(this.disabled){
12719             return;
12720         }
12721         
12722            
12723         if(this.allowBlank && !this.getRawValue().length){
12724             return;
12725         }
12726         if (Roo.bootstrap.version == 3) {
12727             this.el.addClass(this.validClass);
12728         } else {
12729             this.inputEl().addClass('is-valid');
12730         }
12731
12732         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12733             
12734             var feedback = this.el.select('.form-control-feedback', true).first();
12735             
12736             if(feedback){
12737                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12738                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12739             }
12740             
12741         }
12742         
12743         this.fireEvent('valid', this);
12744     },
12745     
12746      /**
12747      * Mark this field as invalid
12748      * @param {String} msg The validation message
12749      */
12750     markInvalid : function(msg)
12751     {
12752         if(!this.el  || this.preventMark){ // not rendered
12753             return;
12754         }
12755         
12756         this.el.removeClass([this.invalidClass, this.validClass]);
12757         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12758         
12759         var feedback = this.el.select('.form-control-feedback', true).first();
12760             
12761         if(feedback){
12762             this.el.select('.form-control-feedback', true).first().removeClass(
12763                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12764         }
12765
12766         if(this.disabled){
12767             return;
12768         }
12769         
12770         if(this.allowBlank && !this.getRawValue().length){
12771             return;
12772         }
12773         
12774         if(this.indicator){
12775             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12776             this.indicator.addClass('visible');
12777         }
12778         if (Roo.bootstrap.version == 3) {
12779             this.el.addClass(this.invalidClass);
12780         } else {
12781             this.inputEl().addClass('is-invalid');
12782         }
12783         
12784         
12785         
12786         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12787             
12788             var feedback = this.el.select('.form-control-feedback', true).first();
12789             
12790             if(feedback){
12791                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12792                 
12793                 if(this.getValue().length || this.forceFeedback){
12794                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12795                 }
12796                 
12797             }
12798             
12799         }
12800         
12801         this.fireEvent('invalid', this, msg);
12802     },
12803     // private
12804     SafariOnKeyDown : function(event)
12805     {
12806         // this is a workaround for a password hang bug on chrome/ webkit.
12807         if (this.inputEl().dom.type != 'password') {
12808             return;
12809         }
12810         
12811         var isSelectAll = false;
12812         
12813         if(this.inputEl().dom.selectionEnd > 0){
12814             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12815         }
12816         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12817             event.preventDefault();
12818             this.setValue('');
12819             return;
12820         }
12821         
12822         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12823             
12824             event.preventDefault();
12825             // this is very hacky as keydown always get's upper case.
12826             //
12827             var cc = String.fromCharCode(event.getCharCode());
12828             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12829             
12830         }
12831     },
12832     adjustWidth : function(tag, w){
12833         tag = tag.toLowerCase();
12834         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12835             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12836                 if(tag == 'input'){
12837                     return w + 2;
12838                 }
12839                 if(tag == 'textarea'){
12840                     return w-2;
12841                 }
12842             }else if(Roo.isOpera){
12843                 if(tag == 'input'){
12844                     return w + 2;
12845                 }
12846                 if(tag == 'textarea'){
12847                     return w-2;
12848                 }
12849             }
12850         }
12851         return w;
12852     },
12853     
12854     setFieldLabel : function(v)
12855     {
12856         if(!this.rendered){
12857             return;
12858         }
12859         
12860         if(this.indicatorEl()){
12861             var ar = this.el.select('label > span',true);
12862             
12863             if (ar.elements.length) {
12864                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12865                 this.fieldLabel = v;
12866                 return;
12867             }
12868             
12869             var br = this.el.select('label',true);
12870             
12871             if(br.elements.length) {
12872                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12873                 this.fieldLabel = v;
12874                 return;
12875             }
12876             
12877             Roo.log('Cannot Found any of label > span || label in input');
12878             return;
12879         }
12880         
12881         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12882         this.fieldLabel = v;
12883         
12884         
12885     }
12886 });
12887
12888  
12889 /*
12890  * - LGPL
12891  *
12892  * Input
12893  * 
12894  */
12895
12896 /**
12897  * @class Roo.bootstrap.TextArea
12898  * @extends Roo.bootstrap.Input
12899  * Bootstrap TextArea class
12900  * @cfg {Number} cols Specifies the visible width of a text area
12901  * @cfg {Number} rows Specifies the visible number of lines in a text area
12902  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12903  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12904  * @cfg {string} html text
12905  * 
12906  * @constructor
12907  * Create a new TextArea
12908  * @param {Object} config The config object
12909  */
12910
12911 Roo.bootstrap.TextArea = function(config){
12912     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12913    
12914 };
12915
12916 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12917      
12918     cols : false,
12919     rows : 5,
12920     readOnly : false,
12921     warp : 'soft',
12922     resize : false,
12923     value: false,
12924     html: false,
12925     
12926     getAutoCreate : function(){
12927         
12928         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12929         
12930         var id = Roo.id();
12931         
12932         var cfg = {};
12933         
12934         if(this.inputType != 'hidden'){
12935             cfg.cls = 'form-group' //input-group
12936         }
12937         
12938         var input =  {
12939             tag: 'textarea',
12940             id : id,
12941             warp : this.warp,
12942             rows : this.rows,
12943             value : this.value || '',
12944             html: this.html || '',
12945             cls : 'form-control',
12946             placeholder : this.placeholder || '' 
12947             
12948         };
12949         
12950         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12951             input.maxLength = this.maxLength;
12952         }
12953         
12954         if(this.resize){
12955             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12956         }
12957         
12958         if(this.cols){
12959             input.cols = this.cols;
12960         }
12961         
12962         if (this.readOnly) {
12963             input.readonly = true;
12964         }
12965         
12966         if (this.name) {
12967             input.name = this.name;
12968         }
12969         
12970         if (this.size) {
12971             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12972         }
12973         
12974         var settings=this;
12975         ['xs','sm','md','lg'].map(function(size){
12976             if (settings[size]) {
12977                 cfg.cls += ' col-' + size + '-' + settings[size];
12978             }
12979         });
12980         
12981         var inputblock = input;
12982         
12983         if(this.hasFeedback && !this.allowBlank){
12984             
12985             var feedback = {
12986                 tag: 'span',
12987                 cls: 'glyphicon form-control-feedback'
12988             };
12989
12990             inputblock = {
12991                 cls : 'has-feedback',
12992                 cn :  [
12993                     input,
12994                     feedback
12995                 ] 
12996             };  
12997         }
12998         
12999         
13000         if (this.before || this.after) {
13001             
13002             inputblock = {
13003                 cls : 'input-group',
13004                 cn :  [] 
13005             };
13006             if (this.before) {
13007                 inputblock.cn.push({
13008                     tag :'span',
13009                     cls : 'input-group-addon',
13010                     html : this.before
13011                 });
13012             }
13013             
13014             inputblock.cn.push(input);
13015             
13016             if(this.hasFeedback && !this.allowBlank){
13017                 inputblock.cls += ' has-feedback';
13018                 inputblock.cn.push(feedback);
13019             }
13020             
13021             if (this.after) {
13022                 inputblock.cn.push({
13023                     tag :'span',
13024                     cls : 'input-group-addon',
13025                     html : this.after
13026                 });
13027             }
13028             
13029         }
13030         
13031         if (align ==='left' && this.fieldLabel.length) {
13032             cfg.cn = [
13033                 {
13034                     tag: 'label',
13035                     'for' :  id,
13036                     cls : 'control-label',
13037                     html : this.fieldLabel
13038                 },
13039                 {
13040                     cls : "",
13041                     cn: [
13042                         inputblock
13043                     ]
13044                 }
13045
13046             ];
13047             
13048             if(this.labelWidth > 12){
13049                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13050             }
13051
13052             if(this.labelWidth < 13 && this.labelmd == 0){
13053                 this.labelmd = this.labelWidth;
13054             }
13055
13056             if(this.labellg > 0){
13057                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13058                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13059             }
13060
13061             if(this.labelmd > 0){
13062                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13063                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13064             }
13065
13066             if(this.labelsm > 0){
13067                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13068                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13069             }
13070
13071             if(this.labelxs > 0){
13072                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13073                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13074             }
13075             
13076         } else if ( this.fieldLabel.length) {
13077             cfg.cn = [
13078
13079                {
13080                    tag: 'label',
13081                    //cls : 'input-group-addon',
13082                    html : this.fieldLabel
13083
13084                },
13085
13086                inputblock
13087
13088            ];
13089
13090         } else {
13091
13092             cfg.cn = [
13093
13094                 inputblock
13095
13096             ];
13097                 
13098         }
13099         
13100         if (this.disabled) {
13101             input.disabled=true;
13102         }
13103         
13104         return cfg;
13105         
13106     },
13107     /**
13108      * return the real textarea element.
13109      */
13110     inputEl: function ()
13111     {
13112         return this.el.select('textarea.form-control',true).first();
13113     },
13114     
13115     /**
13116      * Clear any invalid styles/messages for this field
13117      */
13118     clearInvalid : function()
13119     {
13120         
13121         if(!this.el || this.preventMark){ // not rendered
13122             return;
13123         }
13124         
13125         var label = this.el.select('label', true).first();
13126         var icon = this.el.select('i.fa-star', true).first();
13127         
13128         if(label && icon){
13129             icon.remove();
13130         }
13131         this.el.removeClass( this.validClass);
13132         this.inputEl().removeClass('is-invalid');
13133          
13134         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13135             
13136             var feedback = this.el.select('.form-control-feedback', true).first();
13137             
13138             if(feedback){
13139                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13140             }
13141             
13142         }
13143         
13144         this.fireEvent('valid', this);
13145     },
13146     
13147      /**
13148      * Mark this field as valid
13149      */
13150     markValid : function()
13151     {
13152         if(!this.el  || this.preventMark){ // not rendered
13153             return;
13154         }
13155         
13156         this.el.removeClass([this.invalidClass, this.validClass]);
13157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158         
13159         var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161         if(feedback){
13162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13163         }
13164
13165         if(this.disabled || this.allowBlank){
13166             return;
13167         }
13168         
13169         var label = this.el.select('label', true).first();
13170         var icon = this.el.select('i.fa-star', true).first();
13171         
13172         if(label && icon){
13173             icon.remove();
13174         }
13175         if (Roo.bootstrap.version == 3) {
13176             this.el.addClass(this.validClass);
13177         } else {
13178             this.inputEl().addClass('is-valid');
13179         }
13180         
13181         
13182         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13183             
13184             var feedback = this.el.select('.form-control-feedback', true).first();
13185             
13186             if(feedback){
13187                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13188                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13189             }
13190             
13191         }
13192         
13193         this.fireEvent('valid', this);
13194     },
13195     
13196      /**
13197      * Mark this field as invalid
13198      * @param {String} msg The validation message
13199      */
13200     markInvalid : function(msg)
13201     {
13202         if(!this.el  || this.preventMark){ // not rendered
13203             return;
13204         }
13205         
13206         this.el.removeClass([this.invalidClass, this.validClass]);
13207         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13208         
13209         var feedback = this.el.select('.form-control-feedback', true).first();
13210             
13211         if(feedback){
13212             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13213         }
13214
13215         if(this.disabled || this.allowBlank){
13216             return;
13217         }
13218         
13219         var label = this.el.select('label', true).first();
13220         var icon = this.el.select('i.fa-star', true).first();
13221         
13222         if(!this.getValue().length && label && !icon){
13223             this.el.createChild({
13224                 tag : 'i',
13225                 cls : 'text-danger fa fa-lg fa-star',
13226                 tooltip : 'This field is required',
13227                 style : 'margin-right:5px;'
13228             }, label, true);
13229         }
13230         
13231         if (Roo.bootstrap.version == 3) {
13232             this.el.addClass(this.invalidClass);
13233         } else {
13234             this.inputEl().addClass('is-invalid');
13235         }
13236         
13237         // fixme ... this may be depricated need to test..
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 
13245                 if(this.getValue().length || this.forceFeedback){
13246                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247                 }
13248                 
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('invalid', this, msg);
13254     }
13255 });
13256
13257  
13258 /*
13259  * - LGPL
13260  *
13261  * trigger field - base class for combo..
13262  * 
13263  */
13264  
13265 /**
13266  * @class Roo.bootstrap.TriggerField
13267  * @extends Roo.bootstrap.Input
13268  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13269  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13270  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13271  * for which you can provide a custom implementation.  For example:
13272  * <pre><code>
13273 var trigger = new Roo.bootstrap.TriggerField();
13274 trigger.onTriggerClick = myTriggerFn;
13275 trigger.applyTo('my-field');
13276 </code></pre>
13277  *
13278  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13279  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13280  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13281  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13282  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13283
13284  * @constructor
13285  * Create a new TriggerField.
13286  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13287  * to the base TextField)
13288  */
13289 Roo.bootstrap.TriggerField = function(config){
13290     this.mimicing = false;
13291     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13292 };
13293
13294 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13295     /**
13296      * @cfg {String} triggerClass A CSS class to apply to the trigger
13297      */
13298      /**
13299      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13300      */
13301     hideTrigger:false,
13302
13303     /**
13304      * @cfg {Boolean} removable (true|false) special filter default false
13305      */
13306     removable : false,
13307     
13308     /** @cfg {Boolean} grow @hide */
13309     /** @cfg {Number} growMin @hide */
13310     /** @cfg {Number} growMax @hide */
13311
13312     /**
13313      * @hide 
13314      * @method
13315      */
13316     autoSize: Roo.emptyFn,
13317     // private
13318     monitorTab : true,
13319     // private
13320     deferHeight : true,
13321
13322     
13323     actionMode : 'wrap',
13324     
13325     caret : false,
13326     
13327     
13328     getAutoCreate : function(){
13329        
13330         var align = this.labelAlign || this.parentLabelAlign();
13331         
13332         var id = Roo.id();
13333         
13334         var cfg = {
13335             cls: 'form-group' //input-group
13336         };
13337         
13338         
13339         var input =  {
13340             tag: 'input',
13341             id : id,
13342             type : this.inputType,
13343             cls : 'form-control',
13344             autocomplete: 'new-password',
13345             placeholder : this.placeholder || '' 
13346             
13347         };
13348         if (this.name) {
13349             input.name = this.name;
13350         }
13351         if (this.size) {
13352             input.cls += ' input-' + this.size;
13353         }
13354         
13355         if (this.disabled) {
13356             input.disabled=true;
13357         }
13358         
13359         var inputblock = input;
13360         
13361         if(this.hasFeedback && !this.allowBlank){
13362             
13363             var feedback = {
13364                 tag: 'span',
13365                 cls: 'glyphicon form-control-feedback'
13366             };
13367             
13368             if(this.removable && !this.editable  ){
13369                 inputblock = {
13370                     cls : 'has-feedback',
13371                     cn :  [
13372                         inputblock,
13373                         {
13374                             tag: 'button',
13375                             html : 'x',
13376                             cls : 'roo-combo-removable-btn close'
13377                         },
13378                         feedback
13379                     ] 
13380                 };
13381             } else {
13382                 inputblock = {
13383                     cls : 'has-feedback',
13384                     cn :  [
13385                         inputblock,
13386                         feedback
13387                     ] 
13388                 };
13389             }
13390
13391         } else {
13392             if(this.removable && !this.editable ){
13393                 inputblock = {
13394                     cls : 'roo-removable',
13395                     cn :  [
13396                         inputblock,
13397                         {
13398                             tag: 'button',
13399                             html : 'x',
13400                             cls : 'roo-combo-removable-btn close'
13401                         }
13402                     ] 
13403                 };
13404             }
13405         }
13406         
13407         if (this.before || this.after) {
13408             
13409             inputblock = {
13410                 cls : 'input-group',
13411                 cn :  [] 
13412             };
13413             if (this.before) {
13414                 inputblock.cn.push({
13415                     tag :'span',
13416                     cls : 'input-group-addon input-group-prepend input-group-text',
13417                     html : this.before
13418                 });
13419             }
13420             
13421             inputblock.cn.push(input);
13422             
13423             if(this.hasFeedback && !this.allowBlank){
13424                 inputblock.cls += ' has-feedback';
13425                 inputblock.cn.push(feedback);
13426             }
13427             
13428             if (this.after) {
13429                 inputblock.cn.push({
13430                     tag :'span',
13431                     cls : 'input-group-addon input-group-append input-group-text',
13432                     html : this.after
13433                 });
13434             }
13435             
13436         };
13437         
13438       
13439         
13440         var ibwrap = inputblock;
13441         
13442         if(this.multiple){
13443             ibwrap = {
13444                 tag: 'ul',
13445                 cls: 'roo-select2-choices',
13446                 cn:[
13447                     {
13448                         tag: 'li',
13449                         cls: 'roo-select2-search-field',
13450                         cn: [
13451
13452                             inputblock
13453                         ]
13454                     }
13455                 ]
13456             };
13457                 
13458         }
13459         
13460         var combobox = {
13461             cls: 'roo-select2-container input-group',
13462             cn: [
13463                  {
13464                     tag: 'input',
13465                     type : 'hidden',
13466                     cls: 'form-hidden-field'
13467                 },
13468                 ibwrap
13469             ]
13470         };
13471         
13472         if(!this.multiple && this.showToggleBtn){
13473             
13474             var caret = {
13475                         tag: 'span',
13476                         cls: 'caret'
13477              };
13478             if (this.caret != false) {
13479                 caret = {
13480                      tag: 'i',
13481                      cls: 'fa fa-' + this.caret
13482                 };
13483                 
13484             }
13485             
13486             combobox.cn.push({
13487                 tag :'span',
13488                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13489                 cn : [
13490                     Roo.bootstrap.version == 3 ? caret : '',
13491                     {
13492                         tag: 'span',
13493                         cls: 'combobox-clear',
13494                         cn  : [
13495                             {
13496                                 tag : 'i',
13497                                 cls: 'icon-remove'
13498                             }
13499                         ]
13500                     }
13501                 ]
13502
13503             })
13504         }
13505         
13506         if(this.multiple){
13507             combobox.cls += ' roo-select2-container-multi';
13508         }
13509          var indicator = {
13510             tag : 'i',
13511             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13512             tooltip : 'This field is required'
13513         };
13514         if (Roo.bootstrap.version == 4) {
13515             indicator = {
13516                 tag : 'i',
13517                 style : 'display:none'
13518             };
13519         }
13520         
13521         
13522         if (align ==='left' && this.fieldLabel.length) {
13523             
13524             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13525
13526             cfg.cn = [
13527                 indicator,
13528                 {
13529                     tag: 'label',
13530                     'for' :  id,
13531                     cls : 'control-label',
13532                     html : this.fieldLabel
13533
13534                 },
13535                 {
13536                     cls : "", 
13537                     cn: [
13538                         combobox
13539                     ]
13540                 }
13541
13542             ];
13543             
13544             var labelCfg = cfg.cn[1];
13545             var contentCfg = cfg.cn[2];
13546             
13547             if(this.indicatorpos == 'right'){
13548                 cfg.cn = [
13549                     {
13550                         tag: 'label',
13551                         'for' :  id,
13552                         cls : 'control-label',
13553                         cn : [
13554                             {
13555                                 tag : 'span',
13556                                 html : this.fieldLabel
13557                             },
13558                             indicator
13559                         ]
13560                     },
13561                     {
13562                         cls : "", 
13563                         cn: [
13564                             combobox
13565                         ]
13566                     }
13567
13568                 ];
13569                 
13570                 labelCfg = cfg.cn[0];
13571                 contentCfg = cfg.cn[1];
13572             }
13573             
13574             if(this.labelWidth > 12){
13575                 labelCfg.style = "width: " + this.labelWidth + 'px';
13576             }
13577             
13578             if(this.labelWidth < 13 && this.labelmd == 0){
13579                 this.labelmd = this.labelWidth;
13580             }
13581             
13582             if(this.labellg > 0){
13583                 labelCfg.cls += ' col-lg-' + this.labellg;
13584                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13585             }
13586             
13587             if(this.labelmd > 0){
13588                 labelCfg.cls += ' col-md-' + this.labelmd;
13589                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13590             }
13591             
13592             if(this.labelsm > 0){
13593                 labelCfg.cls += ' col-sm-' + this.labelsm;
13594                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13595             }
13596             
13597             if(this.labelxs > 0){
13598                 labelCfg.cls += ' col-xs-' + this.labelxs;
13599                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13600             }
13601             
13602         } else if ( this.fieldLabel.length) {
13603 //                Roo.log(" label");
13604             cfg.cn = [
13605                 indicator,
13606                {
13607                    tag: 'label',
13608                    //cls : 'input-group-addon',
13609                    html : this.fieldLabel
13610
13611                },
13612
13613                combobox
13614
13615             ];
13616             
13617             if(this.indicatorpos == 'right'){
13618                 
13619                 cfg.cn = [
13620                     {
13621                        tag: 'label',
13622                        cn : [
13623                            {
13624                                tag : 'span',
13625                                html : this.fieldLabel
13626                            },
13627                            indicator
13628                        ]
13629
13630                     },
13631                     combobox
13632
13633                 ];
13634
13635             }
13636
13637         } else {
13638             
13639 //                Roo.log(" no label && no align");
13640                 cfg = combobox
13641                      
13642                 
13643         }
13644         
13645         var settings=this;
13646         ['xs','sm','md','lg'].map(function(size){
13647             if (settings[size]) {
13648                 cfg.cls += ' col-' + size + '-' + settings[size];
13649             }
13650         });
13651         
13652         return cfg;
13653         
13654     },
13655     
13656     
13657     
13658     // private
13659     onResize : function(w, h){
13660 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13661 //        if(typeof w == 'number'){
13662 //            var x = w - this.trigger.getWidth();
13663 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13664 //            this.trigger.setStyle('left', x+'px');
13665 //        }
13666     },
13667
13668     // private
13669     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13670
13671     // private
13672     getResizeEl : function(){
13673         return this.inputEl();
13674     },
13675
13676     // private
13677     getPositionEl : function(){
13678         return this.inputEl();
13679     },
13680
13681     // private
13682     alignErrorIcon : function(){
13683         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13684     },
13685
13686     // private
13687     initEvents : function(){
13688         
13689         this.createList();
13690         
13691         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13692         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13693         if(!this.multiple && this.showToggleBtn){
13694             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13695             if(this.hideTrigger){
13696                 this.trigger.setDisplayed(false);
13697             }
13698             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13699         }
13700         
13701         if(this.multiple){
13702             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13703         }
13704         
13705         if(this.removable && !this.editable && !this.tickable){
13706             var close = this.closeTriggerEl();
13707             
13708             if(close){
13709                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13710                 close.on('click', this.removeBtnClick, this, close);
13711             }
13712         }
13713         
13714         //this.trigger.addClassOnOver('x-form-trigger-over');
13715         //this.trigger.addClassOnClick('x-form-trigger-click');
13716         
13717         //if(!this.width){
13718         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13719         //}
13720     },
13721     
13722     closeTriggerEl : function()
13723     {
13724         var close = this.el.select('.roo-combo-removable-btn', true).first();
13725         return close ? close : false;
13726     },
13727     
13728     removeBtnClick : function(e, h, el)
13729     {
13730         e.preventDefault();
13731         
13732         if(this.fireEvent("remove", this) !== false){
13733             this.reset();
13734             this.fireEvent("afterremove", this)
13735         }
13736     },
13737     
13738     createList : function()
13739     {
13740         this.list = Roo.get(document.body).createChild({
13741             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13742             cls: 'typeahead typeahead-long dropdown-menu shadow',
13743             style: 'display:none'
13744         });
13745         
13746         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13747         
13748     },
13749
13750     // private
13751     initTrigger : function(){
13752        
13753     },
13754
13755     // private
13756     onDestroy : function(){
13757         if(this.trigger){
13758             this.trigger.removeAllListeners();
13759           //  this.trigger.remove();
13760         }
13761         //if(this.wrap){
13762         //    this.wrap.remove();
13763         //}
13764         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13765     },
13766
13767     // private
13768     onFocus : function(){
13769         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13770         /*
13771         if(!this.mimicing){
13772             this.wrap.addClass('x-trigger-wrap-focus');
13773             this.mimicing = true;
13774             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13775             if(this.monitorTab){
13776                 this.el.on("keydown", this.checkTab, this);
13777             }
13778         }
13779         */
13780     },
13781
13782     // private
13783     checkTab : function(e){
13784         if(e.getKey() == e.TAB){
13785             this.triggerBlur();
13786         }
13787     },
13788
13789     // private
13790     onBlur : function(){
13791         // do nothing
13792     },
13793
13794     // private
13795     mimicBlur : function(e, t){
13796         /*
13797         if(!this.wrap.contains(t) && this.validateBlur()){
13798             this.triggerBlur();
13799         }
13800         */
13801     },
13802
13803     // private
13804     triggerBlur : function(){
13805         this.mimicing = false;
13806         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13807         if(this.monitorTab){
13808             this.el.un("keydown", this.checkTab, this);
13809         }
13810         //this.wrap.removeClass('x-trigger-wrap-focus');
13811         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13812     },
13813
13814     // private
13815     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13816     validateBlur : function(e, t){
13817         return true;
13818     },
13819
13820     // private
13821     onDisable : function(){
13822         this.inputEl().dom.disabled = true;
13823         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13824         //if(this.wrap){
13825         //    this.wrap.addClass('x-item-disabled');
13826         //}
13827     },
13828
13829     // private
13830     onEnable : function(){
13831         this.inputEl().dom.disabled = false;
13832         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13833         //if(this.wrap){
13834         //    this.el.removeClass('x-item-disabled');
13835         //}
13836     },
13837
13838     // private
13839     onShow : function(){
13840         var ae = this.getActionEl();
13841         
13842         if(ae){
13843             ae.dom.style.display = '';
13844             ae.dom.style.visibility = 'visible';
13845         }
13846     },
13847
13848     // private
13849     
13850     onHide : function(){
13851         var ae = this.getActionEl();
13852         ae.dom.style.display = 'none';
13853     },
13854
13855     /**
13856      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13857      * by an implementing function.
13858      * @method
13859      * @param {EventObject} e
13860      */
13861     onTriggerClick : Roo.emptyFn
13862 });
13863  
13864 /*
13865 * Licence: LGPL
13866 */
13867
13868 /**
13869  * @class Roo.bootstrap.CardUploader
13870  * @extends Roo.bootstrap.Button
13871  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13872  * @cfg {Number} errorTimeout default 3000
13873  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13874  * @cfg {Array}  html The button text.
13875
13876  *
13877  * @constructor
13878  * Create a new CardUploader
13879  * @param {Object} config The config object
13880  */
13881
13882 Roo.bootstrap.CardUploader = function(config){
13883     
13884  
13885     
13886     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13887     
13888     
13889     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13890         return r.data.id
13891      });
13892     
13893      this.addEvents({
13894          // raw events
13895         /**
13896          * @event preview
13897          * When a image is clicked on - and needs to display a slideshow or similar..
13898          * @param {Roo.bootstrap.Card} this
13899          * @param {Object} The image information data 
13900          *
13901          */
13902         'preview' : true,
13903          /**
13904          * @event download
13905          * When a the download link is clicked
13906          * @param {Roo.bootstrap.Card} this
13907          * @param {Object} The image information data  contains 
13908          */
13909         'download' : true
13910         
13911     });
13912 };
13913  
13914 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13915     
13916      
13917     errorTimeout : 3000,
13918      
13919     images : false,
13920    
13921     fileCollection : false,
13922     allowBlank : true,
13923     
13924     getAutoCreate : function()
13925     {
13926         
13927         var cfg =  {
13928             cls :'form-group' ,
13929             cn : [
13930                
13931                 {
13932                     tag: 'label',
13933                    //cls : 'input-group-addon',
13934                     html : this.fieldLabel
13935
13936                 },
13937
13938                 {
13939                     tag: 'input',
13940                     type : 'hidden',
13941                     name : this.name,
13942                     value : this.value,
13943                     cls : 'd-none  form-control'
13944                 },
13945                 
13946                 {
13947                     tag: 'input',
13948                     multiple : 'multiple',
13949                     type : 'file',
13950                     cls : 'd-none  roo-card-upload-selector'
13951                 },
13952                 
13953                 {
13954                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13955                 },
13956                 {
13957                     cls : 'card-columns roo-card-uploader-container'
13958                 }
13959
13960             ]
13961         };
13962            
13963          
13964         return cfg;
13965     },
13966     
13967     getChildContainer : function() /// what children are added to.
13968     {
13969         return this.containerEl;
13970     },
13971    
13972     getButtonContainer : function() /// what children are added to.
13973     {
13974         return this.el.select(".roo-card-uploader-button-container").first();
13975     },
13976    
13977     initEvents : function()
13978     {
13979         
13980         Roo.bootstrap.Input.prototype.initEvents.call(this);
13981         
13982         var t = this;
13983         this.addxtype({
13984             xns: Roo.bootstrap,
13985
13986             xtype : 'Button',
13987             container_method : 'getButtonContainer' ,            
13988             html :  this.html, // fix changable?
13989             cls : 'w-100 ',
13990             listeners : {
13991                 'click' : function(btn, e) {
13992                     t.onClick(e);
13993                 }
13994             }
13995         });
13996         
13997         
13998         
13999         
14000         this.urlAPI = (window.createObjectURL && window) || 
14001                                 (window.URL && URL.revokeObjectURL && URL) || 
14002                                 (window.webkitURL && webkitURL);
14003                         
14004          
14005          
14006          
14007         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14008         
14009         this.selectorEl.on('change', this.onFileSelected, this);
14010         if (this.images) {
14011             var t = this;
14012             this.images.forEach(function(img) {
14013                 t.addCard(img)
14014             });
14015             this.images = false;
14016         }
14017         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14018          
14019        
14020     },
14021     
14022    
14023     onClick : function(e)
14024     {
14025         e.preventDefault();
14026          
14027         this.selectorEl.dom.click();
14028          
14029     },
14030     
14031     onFileSelected : function(e)
14032     {
14033         e.preventDefault();
14034         
14035         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14036             return;
14037         }
14038         
14039         Roo.each(this.selectorEl.dom.files, function(file){    
14040             this.addFile(file);
14041         }, this);
14042          
14043     },
14044     
14045       
14046     
14047       
14048     
14049     addFile : function(file)
14050     {
14051            
14052         if(typeof(file) === 'string'){
14053             throw "Add file by name?"; // should not happen
14054             return;
14055         }
14056         
14057         if(!file || !this.urlAPI){
14058             return;
14059         }
14060         
14061         // file;
14062         // file.type;
14063         
14064         var _this = this;
14065         
14066         
14067         var url = _this.urlAPI.createObjectURL( file);
14068            
14069         this.addCard({
14070             id : Roo.bootstrap.CardUploader.ID--,
14071             is_uploaded : false,
14072             src : url,
14073             srcfile : file,
14074             title : file.name,
14075             mimetype : file.type,
14076             preview : false,
14077             is_deleted : 0
14078         });
14079         
14080     },
14081     
14082     /**
14083      * addCard - add an Attachment to the uploader
14084      * @param data - the data about the image to upload
14085      *
14086      * {
14087           id : 123
14088           title : "Title of file",
14089           is_uploaded : false,
14090           src : "http://.....",
14091           srcfile : { the File upload object },
14092           mimetype : file.type,
14093           preview : false,
14094           is_deleted : 0
14095           .. any other data...
14096         }
14097      *
14098      * 
14099     */
14100     
14101     addCard : function (data)
14102     {
14103         // hidden input element?
14104         // if the file is not an image...
14105         //then we need to use something other that and header_image
14106         var t = this;
14107         //   remove.....
14108         var footer = [
14109             {
14110                 xns : Roo.bootstrap,
14111                 xtype : 'CardFooter',
14112                  items: [
14113                     {
14114                         xns : Roo.bootstrap,
14115                         xtype : 'Element',
14116                         cls : 'd-flex',
14117                         items : [
14118                             
14119                             {
14120                                 xns : Roo.bootstrap,
14121                                 xtype : 'Button',
14122                                 html : String.format("<small>{0}</small>", data.title),
14123                                 cls : 'col-10 text-left',
14124                                 size: 'sm',
14125                                 weight: 'link',
14126                                 fa : 'download',
14127                                 listeners : {
14128                                     click : function() {
14129                                      
14130                                         t.fireEvent( "download", t, data );
14131                                     }
14132                                 }
14133                             },
14134                           
14135                             {
14136                                 xns : Roo.bootstrap,
14137                                 xtype : 'Button',
14138                                 style: 'max-height: 28px; ',
14139                                 size : 'sm',
14140                                 weight: 'danger',
14141                                 cls : 'col-2',
14142                                 fa : 'times',
14143                                 listeners : {
14144                                     click : function() {
14145                                         t.removeCard(data.id)
14146                                     }
14147                                 }
14148                             }
14149                         ]
14150                     }
14151                     
14152                 ] 
14153             }
14154             
14155         ];
14156         
14157         var cn = this.addxtype(
14158             {
14159                  
14160                 xns : Roo.bootstrap,
14161                 xtype : 'Card',
14162                 closeable : true,
14163                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14164                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14165                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14166                 data : data,
14167                 html : false,
14168                  
14169                 items : footer,
14170                 initEvents : function() {
14171                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14172                     var card = this;
14173                     this.imgEl = this.el.select('.card-img-top').first();
14174                     if (this.imgEl) {
14175                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14176                         this.imgEl.set({ 'pointer' : 'cursor' });
14177                                   
14178                     }
14179                     this.getCardFooter().addClass('p-1');
14180                     
14181                   
14182                 }
14183                 
14184             }
14185         );
14186         // dont' really need ot update items.
14187         // this.items.push(cn);
14188         this.fileCollection.add(cn);
14189         
14190         if (!data.srcfile) {
14191             this.updateInput();
14192             return;
14193         }
14194             
14195         var _t = this;
14196         var reader = new FileReader();
14197         reader.addEventListener("load", function() {  
14198             data.srcdata =  reader.result;
14199             _t.updateInput();
14200         });
14201         reader.readAsDataURL(data.srcfile);
14202         
14203         
14204         
14205     },
14206     removeCard : function(id)
14207     {
14208         
14209         var card  = this.fileCollection.get(id);
14210         card.data.is_deleted = 1;
14211         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14212         //this.fileCollection.remove(card);
14213         //this.items = this.items.filter(function(e) { return e != card });
14214         // dont' really need ot update items.
14215         card.el.dom.parentNode.removeChild(card.el.dom);
14216         this.updateInput();
14217
14218         
14219     },
14220     reset: function()
14221     {
14222         this.fileCollection.each(function(card) {
14223             if (card.el.dom && card.el.dom.parentNode) {
14224                 card.el.dom.parentNode.removeChild(card.el.dom);
14225             }
14226         });
14227         this.fileCollection.clear();
14228         this.updateInput();
14229     },
14230     
14231     updateInput : function()
14232     {
14233          var data = [];
14234         this.fileCollection.each(function(e) {
14235             data.push(e.data);
14236             
14237         });
14238         this.inputEl().dom.value = JSON.stringify(data);
14239         
14240         
14241         
14242     }
14243     
14244     
14245 });
14246
14247
14248 Roo.bootstrap.CardUploader.ID = -1;/*
14249  * Based on:
14250  * Ext JS Library 1.1.1
14251  * Copyright(c) 2006-2007, Ext JS, LLC.
14252  *
14253  * Originally Released Under LGPL - original licence link has changed is not relivant.
14254  *
14255  * Fork - LGPL
14256  * <script type="text/javascript">
14257  */
14258
14259
14260 /**
14261  * @class Roo.data.SortTypes
14262  * @singleton
14263  * Defines the default sorting (casting?) comparison functions used when sorting data.
14264  */
14265 Roo.data.SortTypes = {
14266     /**
14267      * Default sort that does nothing
14268      * @param {Mixed} s The value being converted
14269      * @return {Mixed} The comparison value
14270      */
14271     none : function(s){
14272         return s;
14273     },
14274     
14275     /**
14276      * The regular expression used to strip tags
14277      * @type {RegExp}
14278      * @property
14279      */
14280     stripTagsRE : /<\/?[^>]+>/gi,
14281     
14282     /**
14283      * Strips all HTML tags to sort on text only
14284      * @param {Mixed} s The value being converted
14285      * @return {String} The comparison value
14286      */
14287     asText : function(s){
14288         return String(s).replace(this.stripTagsRE, "");
14289     },
14290     
14291     /**
14292      * Strips all HTML tags to sort on text only - Case insensitive
14293      * @param {Mixed} s The value being converted
14294      * @return {String} The comparison value
14295      */
14296     asUCText : function(s){
14297         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14298     },
14299     
14300     /**
14301      * Case insensitive string
14302      * @param {Mixed} s The value being converted
14303      * @return {String} The comparison value
14304      */
14305     asUCString : function(s) {
14306         return String(s).toUpperCase();
14307     },
14308     
14309     /**
14310      * Date sorting
14311      * @param {Mixed} s The value being converted
14312      * @return {Number} The comparison value
14313      */
14314     asDate : function(s) {
14315         if(!s){
14316             return 0;
14317         }
14318         if(s instanceof Date){
14319             return s.getTime();
14320         }
14321         return Date.parse(String(s));
14322     },
14323     
14324     /**
14325      * Float sorting
14326      * @param {Mixed} s The value being converted
14327      * @return {Float} The comparison value
14328      */
14329     asFloat : function(s) {
14330         var val = parseFloat(String(s).replace(/,/g, ""));
14331         if(isNaN(val)) {
14332             val = 0;
14333         }
14334         return val;
14335     },
14336     
14337     /**
14338      * Integer sorting
14339      * @param {Mixed} s The value being converted
14340      * @return {Number} The comparison value
14341      */
14342     asInt : function(s) {
14343         var val = parseInt(String(s).replace(/,/g, ""));
14344         if(isNaN(val)) {
14345             val = 0;
14346         }
14347         return val;
14348     }
14349 };/*
14350  * Based on:
14351  * Ext JS Library 1.1.1
14352  * Copyright(c) 2006-2007, Ext JS, LLC.
14353  *
14354  * Originally Released Under LGPL - original licence link has changed is not relivant.
14355  *
14356  * Fork - LGPL
14357  * <script type="text/javascript">
14358  */
14359
14360 /**
14361 * @class Roo.data.Record
14362  * Instances of this class encapsulate both record <em>definition</em> information, and record
14363  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14364  * to access Records cached in an {@link Roo.data.Store} object.<br>
14365  * <p>
14366  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14367  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14368  * objects.<br>
14369  * <p>
14370  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14371  * @constructor
14372  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14373  * {@link #create}. The parameters are the same.
14374  * @param {Array} data An associative Array of data values keyed by the field name.
14375  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14376  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14377  * not specified an integer id is generated.
14378  */
14379 Roo.data.Record = function(data, id){
14380     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14381     this.data = data;
14382 };
14383
14384 /**
14385  * Generate a constructor for a specific record layout.
14386  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14387  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14388  * Each field definition object may contain the following properties: <ul>
14389  * <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,
14390  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14391  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14392  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14393  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14394  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14395  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14396  * this may be omitted.</p></li>
14397  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14398  * <ul><li>auto (Default, implies no conversion)</li>
14399  * <li>string</li>
14400  * <li>int</li>
14401  * <li>float</li>
14402  * <li>boolean</li>
14403  * <li>date</li></ul></p></li>
14404  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14405  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14406  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14407  * by the Reader into an object that will be stored in the Record. It is passed the
14408  * following parameters:<ul>
14409  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14410  * </ul></p></li>
14411  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14412  * </ul>
14413  * <br>usage:<br><pre><code>
14414 var TopicRecord = Roo.data.Record.create(
14415     {name: 'title', mapping: 'topic_title'},
14416     {name: 'author', mapping: 'username'},
14417     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14418     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14419     {name: 'lastPoster', mapping: 'user2'},
14420     {name: 'excerpt', mapping: 'post_text'}
14421 );
14422
14423 var myNewRecord = new TopicRecord({
14424     title: 'Do my job please',
14425     author: 'noobie',
14426     totalPosts: 1,
14427     lastPost: new Date(),
14428     lastPoster: 'Animal',
14429     excerpt: 'No way dude!'
14430 });
14431 myStore.add(myNewRecord);
14432 </code></pre>
14433  * @method create
14434  * @static
14435  */
14436 Roo.data.Record.create = function(o){
14437     var f = function(){
14438         f.superclass.constructor.apply(this, arguments);
14439     };
14440     Roo.extend(f, Roo.data.Record);
14441     var p = f.prototype;
14442     p.fields = new Roo.util.MixedCollection(false, function(field){
14443         return field.name;
14444     });
14445     for(var i = 0, len = o.length; i < len; i++){
14446         p.fields.add(new Roo.data.Field(o[i]));
14447     }
14448     f.getField = function(name){
14449         return p.fields.get(name);  
14450     };
14451     return f;
14452 };
14453
14454 Roo.data.Record.AUTO_ID = 1000;
14455 Roo.data.Record.EDIT = 'edit';
14456 Roo.data.Record.REJECT = 'reject';
14457 Roo.data.Record.COMMIT = 'commit';
14458
14459 Roo.data.Record.prototype = {
14460     /**
14461      * Readonly flag - true if this record has been modified.
14462      * @type Boolean
14463      */
14464     dirty : false,
14465     editing : false,
14466     error: null,
14467     modified: null,
14468
14469     // private
14470     join : function(store){
14471         this.store = store;
14472     },
14473
14474     /**
14475      * Set the named field to the specified value.
14476      * @param {String} name The name of the field to set.
14477      * @param {Object} value The value to set the field to.
14478      */
14479     set : function(name, value){
14480         if(this.data[name] == value){
14481             return;
14482         }
14483         this.dirty = true;
14484         if(!this.modified){
14485             this.modified = {};
14486         }
14487         if(typeof this.modified[name] == 'undefined'){
14488             this.modified[name] = this.data[name];
14489         }
14490         this.data[name] = value;
14491         if(!this.editing && this.store){
14492             this.store.afterEdit(this);
14493         }       
14494     },
14495
14496     /**
14497      * Get the value of the named field.
14498      * @param {String} name The name of the field to get the value of.
14499      * @return {Object} The value of the field.
14500      */
14501     get : function(name){
14502         return this.data[name]; 
14503     },
14504
14505     // private
14506     beginEdit : function(){
14507         this.editing = true;
14508         this.modified = {}; 
14509     },
14510
14511     // private
14512     cancelEdit : function(){
14513         this.editing = false;
14514         delete this.modified;
14515     },
14516
14517     // private
14518     endEdit : function(){
14519         this.editing = false;
14520         if(this.dirty && this.store){
14521             this.store.afterEdit(this);
14522         }
14523     },
14524
14525     /**
14526      * Usually called by the {@link Roo.data.Store} which owns the Record.
14527      * Rejects all changes made to the Record since either creation, or the last commit operation.
14528      * Modified fields are reverted to their original values.
14529      * <p>
14530      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14531      * of reject operations.
14532      */
14533     reject : function(){
14534         var m = this.modified;
14535         for(var n in m){
14536             if(typeof m[n] != "function"){
14537                 this.data[n] = m[n];
14538             }
14539         }
14540         this.dirty = false;
14541         delete this.modified;
14542         this.editing = false;
14543         if(this.store){
14544             this.store.afterReject(this);
14545         }
14546     },
14547
14548     /**
14549      * Usually called by the {@link Roo.data.Store} which owns the Record.
14550      * Commits all changes made to the Record since either creation, or the last commit operation.
14551      * <p>
14552      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14553      * of commit operations.
14554      */
14555     commit : function(){
14556         this.dirty = false;
14557         delete this.modified;
14558         this.editing = false;
14559         if(this.store){
14560             this.store.afterCommit(this);
14561         }
14562     },
14563
14564     // private
14565     hasError : function(){
14566         return this.error != null;
14567     },
14568
14569     // private
14570     clearError : function(){
14571         this.error = null;
14572     },
14573
14574     /**
14575      * Creates a copy of this record.
14576      * @param {String} id (optional) A new record id if you don't want to use this record's id
14577      * @return {Record}
14578      */
14579     copy : function(newId) {
14580         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14581     }
14582 };/*
14583  * Based on:
14584  * Ext JS Library 1.1.1
14585  * Copyright(c) 2006-2007, Ext JS, LLC.
14586  *
14587  * Originally Released Under LGPL - original licence link has changed is not relivant.
14588  *
14589  * Fork - LGPL
14590  * <script type="text/javascript">
14591  */
14592
14593
14594
14595 /**
14596  * @class Roo.data.Store
14597  * @extends Roo.util.Observable
14598  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14599  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14600  * <p>
14601  * 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
14602  * has no knowledge of the format of the data returned by the Proxy.<br>
14603  * <p>
14604  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14605  * instances from the data object. These records are cached and made available through accessor functions.
14606  * @constructor
14607  * Creates a new Store.
14608  * @param {Object} config A config object containing the objects needed for the Store to access data,
14609  * and read the data into Records.
14610  */
14611 Roo.data.Store = function(config){
14612     this.data = new Roo.util.MixedCollection(false);
14613     this.data.getKey = function(o){
14614         return o.id;
14615     };
14616     this.baseParams = {};
14617     // private
14618     this.paramNames = {
14619         "start" : "start",
14620         "limit" : "limit",
14621         "sort" : "sort",
14622         "dir" : "dir",
14623         "multisort" : "_multisort"
14624     };
14625
14626     if(config && config.data){
14627         this.inlineData = config.data;
14628         delete config.data;
14629     }
14630
14631     Roo.apply(this, config);
14632     
14633     if(this.reader){ // reader passed
14634         this.reader = Roo.factory(this.reader, Roo.data);
14635         this.reader.xmodule = this.xmodule || false;
14636         if(!this.recordType){
14637             this.recordType = this.reader.recordType;
14638         }
14639         if(this.reader.onMetaChange){
14640             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14641         }
14642     }
14643
14644     if(this.recordType){
14645         this.fields = this.recordType.prototype.fields;
14646     }
14647     this.modified = [];
14648
14649     this.addEvents({
14650         /**
14651          * @event datachanged
14652          * Fires when the data cache has changed, and a widget which is using this Store
14653          * as a Record cache should refresh its view.
14654          * @param {Store} this
14655          */
14656         datachanged : true,
14657         /**
14658          * @event metachange
14659          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14660          * @param {Store} this
14661          * @param {Object} meta The JSON metadata
14662          */
14663         metachange : true,
14664         /**
14665          * @event add
14666          * Fires when Records have been added to the Store
14667          * @param {Store} this
14668          * @param {Roo.data.Record[]} records The array of Records added
14669          * @param {Number} index The index at which the record(s) were added
14670          */
14671         add : true,
14672         /**
14673          * @event remove
14674          * Fires when a Record has been removed from the Store
14675          * @param {Store} this
14676          * @param {Roo.data.Record} record The Record that was removed
14677          * @param {Number} index The index at which the record was removed
14678          */
14679         remove : true,
14680         /**
14681          * @event update
14682          * Fires when a Record has been updated
14683          * @param {Store} this
14684          * @param {Roo.data.Record} record The Record that was updated
14685          * @param {String} operation The update operation being performed.  Value may be one of:
14686          * <pre><code>
14687  Roo.data.Record.EDIT
14688  Roo.data.Record.REJECT
14689  Roo.data.Record.COMMIT
14690          * </code></pre>
14691          */
14692         update : true,
14693         /**
14694          * @event clear
14695          * Fires when the data cache has been cleared.
14696          * @param {Store} this
14697          */
14698         clear : true,
14699         /**
14700          * @event beforeload
14701          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14702          * the load action will be canceled.
14703          * @param {Store} this
14704          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14705          */
14706         beforeload : true,
14707         /**
14708          * @event beforeloadadd
14709          * Fires after a new set of Records has been loaded.
14710          * @param {Store} this
14711          * @param {Roo.data.Record[]} records The Records that were loaded
14712          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14713          */
14714         beforeloadadd : true,
14715         /**
14716          * @event load
14717          * Fires after a new set of Records has been loaded, before they are added to the store.
14718          * @param {Store} this
14719          * @param {Roo.data.Record[]} records The Records that were loaded
14720          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14721          * @params {Object} return from reader
14722          */
14723         load : true,
14724         /**
14725          * @event loadexception
14726          * Fires if an exception occurs in the Proxy during loading.
14727          * Called with the signature of the Proxy's "loadexception" event.
14728          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14729          * 
14730          * @param {Proxy} 
14731          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14732          * @param {Object} load options 
14733          * @param {Object} jsonData from your request (normally this contains the Exception)
14734          */
14735         loadexception : true
14736     });
14737     
14738     if(this.proxy){
14739         this.proxy = Roo.factory(this.proxy, Roo.data);
14740         this.proxy.xmodule = this.xmodule || false;
14741         this.relayEvents(this.proxy,  ["loadexception"]);
14742     }
14743     this.sortToggle = {};
14744     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14745
14746     Roo.data.Store.superclass.constructor.call(this);
14747
14748     if(this.inlineData){
14749         this.loadData(this.inlineData);
14750         delete this.inlineData;
14751     }
14752 };
14753
14754 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14755      /**
14756     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14757     * without a remote query - used by combo/forms at present.
14758     */
14759     
14760     /**
14761     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14762     */
14763     /**
14764     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14765     */
14766     /**
14767     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14768     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14769     */
14770     /**
14771     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14772     * on any HTTP request
14773     */
14774     /**
14775     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14776     */
14777     /**
14778     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14779     */
14780     multiSort: false,
14781     /**
14782     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14783     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14784     */
14785     remoteSort : false,
14786
14787     /**
14788     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14789      * loaded or when a record is removed. (defaults to false).
14790     */
14791     pruneModifiedRecords : false,
14792
14793     // private
14794     lastOptions : null,
14795
14796     /**
14797      * Add Records to the Store and fires the add event.
14798      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14799      */
14800     add : function(records){
14801         records = [].concat(records);
14802         for(var i = 0, len = records.length; i < len; i++){
14803             records[i].join(this);
14804         }
14805         var index = this.data.length;
14806         this.data.addAll(records);
14807         this.fireEvent("add", this, records, index);
14808     },
14809
14810     /**
14811      * Remove a Record from the Store and fires the remove event.
14812      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14813      */
14814     remove : function(record){
14815         var index = this.data.indexOf(record);
14816         this.data.removeAt(index);
14817  
14818         if(this.pruneModifiedRecords){
14819             this.modified.remove(record);
14820         }
14821         this.fireEvent("remove", this, record, index);
14822     },
14823
14824     /**
14825      * Remove all Records from the Store and fires the clear event.
14826      */
14827     removeAll : function(){
14828         this.data.clear();
14829         if(this.pruneModifiedRecords){
14830             this.modified = [];
14831         }
14832         this.fireEvent("clear", this);
14833     },
14834
14835     /**
14836      * Inserts Records to the Store at the given index and fires the add event.
14837      * @param {Number} index The start index at which to insert the passed Records.
14838      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14839      */
14840     insert : function(index, records){
14841         records = [].concat(records);
14842         for(var i = 0, len = records.length; i < len; i++){
14843             this.data.insert(index, records[i]);
14844             records[i].join(this);
14845         }
14846         this.fireEvent("add", this, records, index);
14847     },
14848
14849     /**
14850      * Get the index within the cache of the passed Record.
14851      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14852      * @return {Number} The index of the passed Record. Returns -1 if not found.
14853      */
14854     indexOf : function(record){
14855         return this.data.indexOf(record);
14856     },
14857
14858     /**
14859      * Get the index within the cache of the Record with the passed id.
14860      * @param {String} id The id of the Record to find.
14861      * @return {Number} The index of the Record. Returns -1 if not found.
14862      */
14863     indexOfId : function(id){
14864         return this.data.indexOfKey(id);
14865     },
14866
14867     /**
14868      * Get the Record with the specified id.
14869      * @param {String} id The id of the Record to find.
14870      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14871      */
14872     getById : function(id){
14873         return this.data.key(id);
14874     },
14875
14876     /**
14877      * Get the Record at the specified index.
14878      * @param {Number} index The index of the Record to find.
14879      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14880      */
14881     getAt : function(index){
14882         return this.data.itemAt(index);
14883     },
14884
14885     /**
14886      * Returns a range of Records between specified indices.
14887      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14888      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14889      * @return {Roo.data.Record[]} An array of Records
14890      */
14891     getRange : function(start, end){
14892         return this.data.getRange(start, end);
14893     },
14894
14895     // private
14896     storeOptions : function(o){
14897         o = Roo.apply({}, o);
14898         delete o.callback;
14899         delete o.scope;
14900         this.lastOptions = o;
14901     },
14902
14903     /**
14904      * Loads the Record cache from the configured Proxy using the configured Reader.
14905      * <p>
14906      * If using remote paging, then the first load call must specify the <em>start</em>
14907      * and <em>limit</em> properties in the options.params property to establish the initial
14908      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14909      * <p>
14910      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14911      * and this call will return before the new data has been loaded. Perform any post-processing
14912      * in a callback function, or in a "load" event handler.</strong>
14913      * <p>
14914      * @param {Object} options An object containing properties which control loading options:<ul>
14915      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14916      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14917      * passed the following arguments:<ul>
14918      * <li>r : Roo.data.Record[]</li>
14919      * <li>options: Options object from the load call</li>
14920      * <li>success: Boolean success indicator</li></ul></li>
14921      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14922      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14923      * </ul>
14924      */
14925     load : function(options){
14926         options = options || {};
14927         if(this.fireEvent("beforeload", this, options) !== false){
14928             this.storeOptions(options);
14929             var p = Roo.apply(options.params || {}, this.baseParams);
14930             // if meta was not loaded from remote source.. try requesting it.
14931             if (!this.reader.metaFromRemote) {
14932                 p._requestMeta = 1;
14933             }
14934             if(this.sortInfo && this.remoteSort){
14935                 var pn = this.paramNames;
14936                 p[pn["sort"]] = this.sortInfo.field;
14937                 p[pn["dir"]] = this.sortInfo.direction;
14938             }
14939             if (this.multiSort) {
14940                 var pn = this.paramNames;
14941                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14942             }
14943             
14944             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14945         }
14946     },
14947
14948     /**
14949      * Reloads the Record cache from the configured Proxy using the configured Reader and
14950      * the options from the last load operation performed.
14951      * @param {Object} options (optional) An object containing properties which may override the options
14952      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14953      * the most recently used options are reused).
14954      */
14955     reload : function(options){
14956         this.load(Roo.applyIf(options||{}, this.lastOptions));
14957     },
14958
14959     // private
14960     // Called as a callback by the Reader during a load operation.
14961     loadRecords : function(o, options, success){
14962         if(!o || success === false){
14963             if(success !== false){
14964                 this.fireEvent("load", this, [], options, o);
14965             }
14966             if(options.callback){
14967                 options.callback.call(options.scope || this, [], options, false);
14968             }
14969             return;
14970         }
14971         // if data returned failure - throw an exception.
14972         if (o.success === false) {
14973             // show a message if no listener is registered.
14974             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14975                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14976             }
14977             // loadmask wil be hooked into this..
14978             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14979             return;
14980         }
14981         var r = o.records, t = o.totalRecords || r.length;
14982         
14983         this.fireEvent("beforeloadadd", this, r, options, o);
14984         
14985         if(!options || options.add !== true){
14986             if(this.pruneModifiedRecords){
14987                 this.modified = [];
14988             }
14989             for(var i = 0, len = r.length; i < len; i++){
14990                 r[i].join(this);
14991             }
14992             if(this.snapshot){
14993                 this.data = this.snapshot;
14994                 delete this.snapshot;
14995             }
14996             this.data.clear();
14997             this.data.addAll(r);
14998             this.totalLength = t;
14999             this.applySort();
15000             this.fireEvent("datachanged", this);
15001         }else{
15002             this.totalLength = Math.max(t, this.data.length+r.length);
15003             this.add(r);
15004         }
15005         
15006         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15007                 
15008             var e = new Roo.data.Record({});
15009
15010             e.set(this.parent.displayField, this.parent.emptyTitle);
15011             e.set(this.parent.valueField, '');
15012
15013             this.insert(0, e);
15014         }
15015             
15016         this.fireEvent("load", this, r, options, o);
15017         if(options.callback){
15018             options.callback.call(options.scope || this, r, options, true);
15019         }
15020     },
15021
15022
15023     /**
15024      * Loads data from a passed data block. A Reader which understands the format of the data
15025      * must have been configured in the constructor.
15026      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15027      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15028      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15029      */
15030     loadData : function(o, append){
15031         var r = this.reader.readRecords(o);
15032         this.loadRecords(r, {add: append}, true);
15033     },
15034     
15035      /**
15036      * using 'cn' the nested child reader read the child array into it's child stores.
15037      * @param {Object} rec The record with a 'children array
15038      */
15039     loadDataFromChildren : function(rec)
15040     {
15041         this.loadData(this.reader.toLoadData(rec));
15042     },
15043     
15044
15045     /**
15046      * Gets the number of cached records.
15047      * <p>
15048      * <em>If using paging, this may not be the total size of the dataset. If the data object
15049      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15050      * the data set size</em>
15051      */
15052     getCount : function(){
15053         return this.data.length || 0;
15054     },
15055
15056     /**
15057      * Gets the total number of records in the dataset as returned by the server.
15058      * <p>
15059      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15060      * the dataset size</em>
15061      */
15062     getTotalCount : function(){
15063         return this.totalLength || 0;
15064     },
15065
15066     /**
15067      * Returns the sort state of the Store as an object with two properties:
15068      * <pre><code>
15069  field {String} The name of the field by which the Records are sorted
15070  direction {String} The sort order, "ASC" or "DESC"
15071      * </code></pre>
15072      */
15073     getSortState : function(){
15074         return this.sortInfo;
15075     },
15076
15077     // private
15078     applySort : function(){
15079         if(this.sortInfo && !this.remoteSort){
15080             var s = this.sortInfo, f = s.field;
15081             var st = this.fields.get(f).sortType;
15082             var fn = function(r1, r2){
15083                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15084                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15085             };
15086             this.data.sort(s.direction, fn);
15087             if(this.snapshot && this.snapshot != this.data){
15088                 this.snapshot.sort(s.direction, fn);
15089             }
15090         }
15091     },
15092
15093     /**
15094      * Sets the default sort column and order to be used by the next load operation.
15095      * @param {String} fieldName The name of the field to sort by.
15096      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15097      */
15098     setDefaultSort : function(field, dir){
15099         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15100     },
15101
15102     /**
15103      * Sort the Records.
15104      * If remote sorting is used, the sort is performed on the server, and the cache is
15105      * reloaded. If local sorting is used, the cache is sorted internally.
15106      * @param {String} fieldName The name of the field to sort by.
15107      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15108      */
15109     sort : function(fieldName, dir){
15110         var f = this.fields.get(fieldName);
15111         if(!dir){
15112             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15113             
15114             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15115                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15116             }else{
15117                 dir = f.sortDir;
15118             }
15119         }
15120         this.sortToggle[f.name] = dir;
15121         this.sortInfo = {field: f.name, direction: dir};
15122         if(!this.remoteSort){
15123             this.applySort();
15124             this.fireEvent("datachanged", this);
15125         }else{
15126             this.load(this.lastOptions);
15127         }
15128     },
15129
15130     /**
15131      * Calls the specified function for each of the Records in the cache.
15132      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15133      * Returning <em>false</em> aborts and exits the iteration.
15134      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15135      */
15136     each : function(fn, scope){
15137         this.data.each(fn, scope);
15138     },
15139
15140     /**
15141      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15142      * (e.g., during paging).
15143      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15144      */
15145     getModifiedRecords : function(){
15146         return this.modified;
15147     },
15148
15149     // private
15150     createFilterFn : function(property, value, anyMatch){
15151         if(!value.exec){ // not a regex
15152             value = String(value);
15153             if(value.length == 0){
15154                 return false;
15155             }
15156             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15157         }
15158         return function(r){
15159             return value.test(r.data[property]);
15160         };
15161     },
15162
15163     /**
15164      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15165      * @param {String} property A field on your records
15166      * @param {Number} start The record index to start at (defaults to 0)
15167      * @param {Number} end The last record index to include (defaults to length - 1)
15168      * @return {Number} The sum
15169      */
15170     sum : function(property, start, end){
15171         var rs = this.data.items, v = 0;
15172         start = start || 0;
15173         end = (end || end === 0) ? end : rs.length-1;
15174
15175         for(var i = start; i <= end; i++){
15176             v += (rs[i].data[property] || 0);
15177         }
15178         return v;
15179     },
15180
15181     /**
15182      * Filter the records by a specified property.
15183      * @param {String} field A field on your records
15184      * @param {String/RegExp} value Either a string that the field
15185      * should start with or a RegExp to test against the field
15186      * @param {Boolean} anyMatch True to match any part not just the beginning
15187      */
15188     filter : function(property, value, anyMatch){
15189         var fn = this.createFilterFn(property, value, anyMatch);
15190         return fn ? this.filterBy(fn) : this.clearFilter();
15191     },
15192
15193     /**
15194      * Filter by a function. The specified function will be called with each
15195      * record in this data source. If the function returns true the record is included,
15196      * otherwise it is filtered.
15197      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15198      * @param {Object} scope (optional) The scope of the function (defaults to this)
15199      */
15200     filterBy : function(fn, scope){
15201         this.snapshot = this.snapshot || this.data;
15202         this.data = this.queryBy(fn, scope||this);
15203         this.fireEvent("datachanged", this);
15204     },
15205
15206     /**
15207      * Query the records by a specified property.
15208      * @param {String} field A field on your records
15209      * @param {String/RegExp} value Either a string that the field
15210      * should start with or a RegExp to test against the field
15211      * @param {Boolean} anyMatch True to match any part not just the beginning
15212      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15213      */
15214     query : function(property, value, anyMatch){
15215         var fn = this.createFilterFn(property, value, anyMatch);
15216         return fn ? this.queryBy(fn) : this.data.clone();
15217     },
15218
15219     /**
15220      * Query by a function. The specified function will be called with each
15221      * record in this data source. If the function returns true the record is included
15222      * in the results.
15223      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15224      * @param {Object} scope (optional) The scope of the function (defaults to this)
15225       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15226      **/
15227     queryBy : function(fn, scope){
15228         var data = this.snapshot || this.data;
15229         return data.filterBy(fn, scope||this);
15230     },
15231
15232     /**
15233      * Collects unique values for a particular dataIndex from this store.
15234      * @param {String} dataIndex The property to collect
15235      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15236      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15237      * @return {Array} An array of the unique values
15238      **/
15239     collect : function(dataIndex, allowNull, bypassFilter){
15240         var d = (bypassFilter === true && this.snapshot) ?
15241                 this.snapshot.items : this.data.items;
15242         var v, sv, r = [], l = {};
15243         for(var i = 0, len = d.length; i < len; i++){
15244             v = d[i].data[dataIndex];
15245             sv = String(v);
15246             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15247                 l[sv] = true;
15248                 r[r.length] = v;
15249             }
15250         }
15251         return r;
15252     },
15253
15254     /**
15255      * Revert to a view of the Record cache with no filtering applied.
15256      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15257      */
15258     clearFilter : function(suppressEvent){
15259         if(this.snapshot && this.snapshot != this.data){
15260             this.data = this.snapshot;
15261             delete this.snapshot;
15262             if(suppressEvent !== true){
15263                 this.fireEvent("datachanged", this);
15264             }
15265         }
15266     },
15267
15268     // private
15269     afterEdit : function(record){
15270         if(this.modified.indexOf(record) == -1){
15271             this.modified.push(record);
15272         }
15273         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15274     },
15275     
15276     // private
15277     afterReject : function(record){
15278         this.modified.remove(record);
15279         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15280     },
15281
15282     // private
15283     afterCommit : function(record){
15284         this.modified.remove(record);
15285         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15286     },
15287
15288     /**
15289      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15290      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15291      */
15292     commitChanges : function(){
15293         var m = this.modified.slice(0);
15294         this.modified = [];
15295         for(var i = 0, len = m.length; i < len; i++){
15296             m[i].commit();
15297         }
15298     },
15299
15300     /**
15301      * Cancel outstanding changes on all changed records.
15302      */
15303     rejectChanges : function(){
15304         var m = this.modified.slice(0);
15305         this.modified = [];
15306         for(var i = 0, len = m.length; i < len; i++){
15307             m[i].reject();
15308         }
15309     },
15310
15311     onMetaChange : function(meta, rtype, o){
15312         this.recordType = rtype;
15313         this.fields = rtype.prototype.fields;
15314         delete this.snapshot;
15315         this.sortInfo = meta.sortInfo || this.sortInfo;
15316         this.modified = [];
15317         this.fireEvent('metachange', this, this.reader.meta);
15318     },
15319     
15320     moveIndex : function(data, type)
15321     {
15322         var index = this.indexOf(data);
15323         
15324         var newIndex = index + type;
15325         
15326         this.remove(data);
15327         
15328         this.insert(newIndex, data);
15329         
15330     }
15331 });/*
15332  * Based on:
15333  * Ext JS Library 1.1.1
15334  * Copyright(c) 2006-2007, Ext JS, LLC.
15335  *
15336  * Originally Released Under LGPL - original licence link has changed is not relivant.
15337  *
15338  * Fork - LGPL
15339  * <script type="text/javascript">
15340  */
15341
15342 /**
15343  * @class Roo.data.SimpleStore
15344  * @extends Roo.data.Store
15345  * Small helper class to make creating Stores from Array data easier.
15346  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15347  * @cfg {Array} fields An array of field definition objects, or field name strings.
15348  * @cfg {Object} an existing reader (eg. copied from another store)
15349  * @cfg {Array} data The multi-dimensional array of data
15350  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15351  * @cfg {Roo.data.Reader} reader  [not-required] 
15352  * @constructor
15353  * @param {Object} config
15354  */
15355 Roo.data.SimpleStore = function(config)
15356 {
15357     Roo.data.SimpleStore.superclass.constructor.call(this, {
15358         isLocal : true,
15359         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15360                 id: config.id
15361             },
15362             Roo.data.Record.create(config.fields)
15363         ),
15364         proxy : new Roo.data.MemoryProxy(config.data)
15365     });
15366     this.load();
15367 };
15368 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15369  * Based on:
15370  * Ext JS Library 1.1.1
15371  * Copyright(c) 2006-2007, Ext JS, LLC.
15372  *
15373  * Originally Released Under LGPL - original licence link has changed is not relivant.
15374  *
15375  * Fork - LGPL
15376  * <script type="text/javascript">
15377  */
15378
15379 /**
15380 /**
15381  * @extends Roo.data.Store
15382  * @class Roo.data.JsonStore
15383  * Small helper class to make creating Stores for JSON data easier. <br/>
15384 <pre><code>
15385 var store = new Roo.data.JsonStore({
15386     url: 'get-images.php',
15387     root: 'images',
15388     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15389 });
15390 </code></pre>
15391  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15392  * JsonReader and HttpProxy (unless inline data is provided).</b>
15393  * @cfg {Array} fields An array of field definition objects, or field name strings.
15394  * @constructor
15395  * @param {Object} config
15396  */
15397 Roo.data.JsonStore = function(c){
15398     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15399         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15400         reader: new Roo.data.JsonReader(c, c.fields)
15401     }));
15402 };
15403 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15404  * Based on:
15405  * Ext JS Library 1.1.1
15406  * Copyright(c) 2006-2007, Ext JS, LLC.
15407  *
15408  * Originally Released Under LGPL - original licence link has changed is not relivant.
15409  *
15410  * Fork - LGPL
15411  * <script type="text/javascript">
15412  */
15413
15414  
15415 Roo.data.Field = function(config){
15416     if(typeof config == "string"){
15417         config = {name: config};
15418     }
15419     Roo.apply(this, config);
15420     
15421     if(!this.type){
15422         this.type = "auto";
15423     }
15424     
15425     var st = Roo.data.SortTypes;
15426     // named sortTypes are supported, here we look them up
15427     if(typeof this.sortType == "string"){
15428         this.sortType = st[this.sortType];
15429     }
15430     
15431     // set default sortType for strings and dates
15432     if(!this.sortType){
15433         switch(this.type){
15434             case "string":
15435                 this.sortType = st.asUCString;
15436                 break;
15437             case "date":
15438                 this.sortType = st.asDate;
15439                 break;
15440             default:
15441                 this.sortType = st.none;
15442         }
15443     }
15444
15445     // define once
15446     var stripRe = /[\$,%]/g;
15447
15448     // prebuilt conversion function for this field, instead of
15449     // switching every time we're reading a value
15450     if(!this.convert){
15451         var cv, dateFormat = this.dateFormat;
15452         switch(this.type){
15453             case "":
15454             case "auto":
15455             case undefined:
15456                 cv = function(v){ return v; };
15457                 break;
15458             case "string":
15459                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15460                 break;
15461             case "int":
15462                 cv = function(v){
15463                     return v !== undefined && v !== null && v !== '' ?
15464                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15465                     };
15466                 break;
15467             case "float":
15468                 cv = function(v){
15469                     return v !== undefined && v !== null && v !== '' ?
15470                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15471                     };
15472                 break;
15473             case "bool":
15474             case "boolean":
15475                 cv = function(v){ return v === true || v === "true" || v == 1; };
15476                 break;
15477             case "date":
15478                 cv = function(v){
15479                     if(!v){
15480                         return '';
15481                     }
15482                     if(v instanceof Date){
15483                         return v;
15484                     }
15485                     if(dateFormat){
15486                         if(dateFormat == "timestamp"){
15487                             return new Date(v*1000);
15488                         }
15489                         return Date.parseDate(v, dateFormat);
15490                     }
15491                     var parsed = Date.parse(v);
15492                     return parsed ? new Date(parsed) : null;
15493                 };
15494              break;
15495             
15496         }
15497         this.convert = cv;
15498     }
15499 };
15500
15501 Roo.data.Field.prototype = {
15502     dateFormat: null,
15503     defaultValue: "",
15504     mapping: null,
15505     sortType : null,
15506     sortDir : "ASC"
15507 };/*
15508  * Based on:
15509  * Ext JS Library 1.1.1
15510  * Copyright(c) 2006-2007, Ext JS, LLC.
15511  *
15512  * Originally Released Under LGPL - original licence link has changed is not relivant.
15513  *
15514  * Fork - LGPL
15515  * <script type="text/javascript">
15516  */
15517  
15518 // Base class for reading structured data from a data source.  This class is intended to be
15519 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15520
15521 /**
15522  * @class Roo.data.DataReader
15523  * @abstract
15524  * Base class for reading structured data from a data source.  This class is intended to be
15525  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15526  */
15527
15528 Roo.data.DataReader = function(meta, recordType){
15529     
15530     this.meta = meta;
15531     
15532     this.recordType = recordType instanceof Array ? 
15533         Roo.data.Record.create(recordType) : recordType;
15534 };
15535
15536 Roo.data.DataReader.prototype = {
15537     
15538     
15539     readerType : 'Data',
15540      /**
15541      * Create an empty record
15542      * @param {Object} data (optional) - overlay some values
15543      * @return {Roo.data.Record} record created.
15544      */
15545     newRow :  function(d) {
15546         var da =  {};
15547         this.recordType.prototype.fields.each(function(c) {
15548             switch( c.type) {
15549                 case 'int' : da[c.name] = 0; break;
15550                 case 'date' : da[c.name] = new Date(); break;
15551                 case 'float' : da[c.name] = 0.0; break;
15552                 case 'boolean' : da[c.name] = false; break;
15553                 default : da[c.name] = ""; break;
15554             }
15555             
15556         });
15557         return new this.recordType(Roo.apply(da, d));
15558     }
15559     
15560     
15561 };/*
15562  * Based on:
15563  * Ext JS Library 1.1.1
15564  * Copyright(c) 2006-2007, Ext JS, LLC.
15565  *
15566  * Originally Released Under LGPL - original licence link has changed is not relivant.
15567  *
15568  * Fork - LGPL
15569  * <script type="text/javascript">
15570  */
15571
15572 /**
15573  * @class Roo.data.DataProxy
15574  * @extends Roo.data.Observable
15575  * @abstract
15576  * This class is an abstract base class for implementations which provide retrieval of
15577  * unformatted data objects.<br>
15578  * <p>
15579  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15580  * (of the appropriate type which knows how to parse the data object) to provide a block of
15581  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15582  * <p>
15583  * Custom implementations must implement the load method as described in
15584  * {@link Roo.data.HttpProxy#load}.
15585  */
15586 Roo.data.DataProxy = function(){
15587     this.addEvents({
15588         /**
15589          * @event beforeload
15590          * Fires before a network request is made to retrieve a data object.
15591          * @param {Object} This DataProxy object.
15592          * @param {Object} params The params parameter to the load function.
15593          */
15594         beforeload : true,
15595         /**
15596          * @event load
15597          * Fires before the load method's callback is called.
15598          * @param {Object} This DataProxy object.
15599          * @param {Object} o The data object.
15600          * @param {Object} arg The callback argument object passed to the load function.
15601          */
15602         load : true,
15603         /**
15604          * @event loadexception
15605          * Fires if an Exception occurs during data retrieval.
15606          * @param {Object} This DataProxy object.
15607          * @param {Object} o The data object.
15608          * @param {Object} arg The callback argument object passed to the load function.
15609          * @param {Object} e The Exception.
15610          */
15611         loadexception : true
15612     });
15613     Roo.data.DataProxy.superclass.constructor.call(this);
15614 };
15615
15616 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15617
15618     /**
15619      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15620      */
15621 /*
15622  * Based on:
15623  * Ext JS Library 1.1.1
15624  * Copyright(c) 2006-2007, Ext JS, LLC.
15625  *
15626  * Originally Released Under LGPL - original licence link has changed is not relivant.
15627  *
15628  * Fork - LGPL
15629  * <script type="text/javascript">
15630  */
15631 /**
15632  * @class Roo.data.MemoryProxy
15633  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15634  * to the Reader when its load method is called.
15635  * @constructor
15636  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15637  */
15638 Roo.data.MemoryProxy = function(data){
15639     if (data.data) {
15640         data = data.data;
15641     }
15642     Roo.data.MemoryProxy.superclass.constructor.call(this);
15643     this.data = data;
15644 };
15645
15646 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15647     
15648     /**
15649      * Load data from the requested source (in this case an in-memory
15650      * data object passed to the constructor), read the data object into
15651      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15652      * process that block using the passed callback.
15653      * @param {Object} params This parameter is not used by the MemoryProxy class.
15654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15655      * object into a block of Roo.data.Records.
15656      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15657      * The function must be passed <ul>
15658      * <li>The Record block object</li>
15659      * <li>The "arg" argument from the load function</li>
15660      * <li>A boolean success indicator</li>
15661      * </ul>
15662      * @param {Object} scope The scope in which to call the callback
15663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15664      */
15665     load : function(params, reader, callback, scope, arg){
15666         params = params || {};
15667         var result;
15668         try {
15669             result = reader.readRecords(params.data ? params.data :this.data);
15670         }catch(e){
15671             this.fireEvent("loadexception", this, arg, null, e);
15672             callback.call(scope, null, arg, false);
15673             return;
15674         }
15675         callback.call(scope, result, arg, true);
15676     },
15677     
15678     // private
15679     update : function(params, records){
15680         
15681     }
15682 });/*
15683  * Based on:
15684  * Ext JS Library 1.1.1
15685  * Copyright(c) 2006-2007, Ext JS, LLC.
15686  *
15687  * Originally Released Under LGPL - original licence link has changed is not relivant.
15688  *
15689  * Fork - LGPL
15690  * <script type="text/javascript">
15691  */
15692 /**
15693  * @class Roo.data.HttpProxy
15694  * @extends Roo.data.DataProxy
15695  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15696  * configured to reference a certain URL.<br><br>
15697  * <p>
15698  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15699  * from which the running page was served.<br><br>
15700  * <p>
15701  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15702  * <p>
15703  * Be aware that to enable the browser to parse an XML document, the server must set
15704  * the Content-Type header in the HTTP response to "text/xml".
15705  * @constructor
15706  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15707  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15708  * will be used to make the request.
15709  */
15710 Roo.data.HttpProxy = function(conn){
15711     Roo.data.HttpProxy.superclass.constructor.call(this);
15712     // is conn a conn config or a real conn?
15713     this.conn = conn;
15714     this.useAjax = !conn || !conn.events;
15715   
15716 };
15717
15718 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15719     // thse are take from connection...
15720     
15721     /**
15722      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15723      */
15724     /**
15725      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15726      * extra parameters to each request made by this object. (defaults to undefined)
15727      */
15728     /**
15729      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15730      *  to each request made by this object. (defaults to undefined)
15731      */
15732     /**
15733      * @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)
15734      */
15735     /**
15736      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15737      */
15738      /**
15739      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15740      * @type Boolean
15741      */
15742   
15743
15744     /**
15745      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15746      * @type Boolean
15747      */
15748     /**
15749      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15750      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15751      * a finer-grained basis than the DataProxy events.
15752      */
15753     getConnection : function(){
15754         return this.useAjax ? Roo.Ajax : this.conn;
15755     },
15756
15757     /**
15758      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15759      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15760      * process that block using the passed callback.
15761      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15762      * for the request to the remote server.
15763      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15764      * object into a block of Roo.data.Records.
15765      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15766      * The function must be passed <ul>
15767      * <li>The Record block object</li>
15768      * <li>The "arg" argument from the load function</li>
15769      * <li>A boolean success indicator</li>
15770      * </ul>
15771      * @param {Object} scope The scope in which to call the callback
15772      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15773      */
15774     load : function(params, reader, callback, scope, arg){
15775         if(this.fireEvent("beforeload", this, params) !== false){
15776             var  o = {
15777                 params : params || {},
15778                 request: {
15779                     callback : callback,
15780                     scope : scope,
15781                     arg : arg
15782                 },
15783                 reader: reader,
15784                 callback : this.loadResponse,
15785                 scope: this
15786             };
15787             if(this.useAjax){
15788                 Roo.applyIf(o, this.conn);
15789                 if(this.activeRequest){
15790                     Roo.Ajax.abort(this.activeRequest);
15791                 }
15792                 this.activeRequest = Roo.Ajax.request(o);
15793             }else{
15794                 this.conn.request(o);
15795             }
15796         }else{
15797             callback.call(scope||this, null, arg, false);
15798         }
15799     },
15800
15801     // private
15802     loadResponse : function(o, success, response){
15803         delete this.activeRequest;
15804         if(!success){
15805             this.fireEvent("loadexception", this, o, response);
15806             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15807             return;
15808         }
15809         var result;
15810         try {
15811             result = o.reader.read(response);
15812         }catch(e){
15813             this.fireEvent("loadexception", this, o, response, e);
15814             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15815             return;
15816         }
15817         
15818         this.fireEvent("load", this, o, o.request.arg);
15819         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15820     },
15821
15822     // private
15823     update : function(dataSet){
15824
15825     },
15826
15827     // private
15828     updateResponse : function(dataSet){
15829
15830     }
15831 });/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843  * @class Roo.data.ScriptTagProxy
15844  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15845  * other than the originating domain of the running page.<br><br>
15846  * <p>
15847  * <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
15848  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15849  * <p>
15850  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15851  * source code that is used as the source inside a &lt;script> tag.<br><br>
15852  * <p>
15853  * In order for the browser to process the returned data, the server must wrap the data object
15854  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15855  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15856  * depending on whether the callback name was passed:
15857  * <p>
15858  * <pre><code>
15859 boolean scriptTag = false;
15860 String cb = request.getParameter("callback");
15861 if (cb != null) {
15862     scriptTag = true;
15863     response.setContentType("text/javascript");
15864 } else {
15865     response.setContentType("application/x-json");
15866 }
15867 Writer out = response.getWriter();
15868 if (scriptTag) {
15869     out.write(cb + "(");
15870 }
15871 out.print(dataBlock.toJsonString());
15872 if (scriptTag) {
15873     out.write(");");
15874 }
15875 </pre></code>
15876  *
15877  * @constructor
15878  * @param {Object} config A configuration object.
15879  */
15880 Roo.data.ScriptTagProxy = function(config){
15881     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15882     Roo.apply(this, config);
15883     this.head = document.getElementsByTagName("head")[0];
15884 };
15885
15886 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15887
15888 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15889     /**
15890      * @cfg {String} url The URL from which to request the data object.
15891      */
15892     /**
15893      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15894      */
15895     timeout : 30000,
15896     /**
15897      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15898      * the server the name of the callback function set up by the load call to process the returned data object.
15899      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15900      * javascript output which calls this named function passing the data object as its only parameter.
15901      */
15902     callbackParam : "callback",
15903     /**
15904      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15905      * name to the request.
15906      */
15907     nocache : true,
15908
15909     /**
15910      * Load data from the configured URL, read the data object into
15911      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15912      * process that block using the passed callback.
15913      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15914      * for the request to the remote server.
15915      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15916      * object into a block of Roo.data.Records.
15917      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15918      * The function must be passed <ul>
15919      * <li>The Record block object</li>
15920      * <li>The "arg" argument from the load function</li>
15921      * <li>A boolean success indicator</li>
15922      * </ul>
15923      * @param {Object} scope The scope in which to call the callback
15924      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15925      */
15926     load : function(params, reader, callback, scope, arg){
15927         if(this.fireEvent("beforeload", this, params) !== false){
15928
15929             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15930
15931             var url = this.url;
15932             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15933             if(this.nocache){
15934                 url += "&_dc=" + (new Date().getTime());
15935             }
15936             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15937             var trans = {
15938                 id : transId,
15939                 cb : "stcCallback"+transId,
15940                 scriptId : "stcScript"+transId,
15941                 params : params,
15942                 arg : arg,
15943                 url : url,
15944                 callback : callback,
15945                 scope : scope,
15946                 reader : reader
15947             };
15948             var conn = this;
15949
15950             window[trans.cb] = function(o){
15951                 conn.handleResponse(o, trans);
15952             };
15953
15954             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15955
15956             if(this.autoAbort !== false){
15957                 this.abort();
15958             }
15959
15960             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15961
15962             var script = document.createElement("script");
15963             script.setAttribute("src", url);
15964             script.setAttribute("type", "text/javascript");
15965             script.setAttribute("id", trans.scriptId);
15966             this.head.appendChild(script);
15967
15968             this.trans = trans;
15969         }else{
15970             callback.call(scope||this, null, arg, false);
15971         }
15972     },
15973
15974     // private
15975     isLoading : function(){
15976         return this.trans ? true : false;
15977     },
15978
15979     /**
15980      * Abort the current server request.
15981      */
15982     abort : function(){
15983         if(this.isLoading()){
15984             this.destroyTrans(this.trans);
15985         }
15986     },
15987
15988     // private
15989     destroyTrans : function(trans, isLoaded){
15990         this.head.removeChild(document.getElementById(trans.scriptId));
15991         clearTimeout(trans.timeoutId);
15992         if(isLoaded){
15993             window[trans.cb] = undefined;
15994             try{
15995                 delete window[trans.cb];
15996             }catch(e){}
15997         }else{
15998             // if hasn't been loaded, wait for load to remove it to prevent script error
15999             window[trans.cb] = function(){
16000                 window[trans.cb] = undefined;
16001                 try{
16002                     delete window[trans.cb];
16003                 }catch(e){}
16004             };
16005         }
16006     },
16007
16008     // private
16009     handleResponse : function(o, trans){
16010         this.trans = false;
16011         this.destroyTrans(trans, true);
16012         var result;
16013         try {
16014             result = trans.reader.readRecords(o);
16015         }catch(e){
16016             this.fireEvent("loadexception", this, o, trans.arg, e);
16017             trans.callback.call(trans.scope||window, null, trans.arg, false);
16018             return;
16019         }
16020         this.fireEvent("load", this, o, trans.arg);
16021         trans.callback.call(trans.scope||window, result, trans.arg, true);
16022     },
16023
16024     // private
16025     handleFailure : function(trans){
16026         this.trans = false;
16027         this.destroyTrans(trans, false);
16028         this.fireEvent("loadexception", this, null, trans.arg);
16029         trans.callback.call(trans.scope||window, null, trans.arg, false);
16030     }
16031 });/*
16032  * Based on:
16033  * Ext JS Library 1.1.1
16034  * Copyright(c) 2006-2007, Ext JS, LLC.
16035  *
16036  * Originally Released Under LGPL - original licence link has changed is not relivant.
16037  *
16038  * Fork - LGPL
16039  * <script type="text/javascript">
16040  */
16041
16042 /**
16043  * @class Roo.data.JsonReader
16044  * @extends Roo.data.DataReader
16045  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16046  * based on mappings in a provided Roo.data.Record constructor.
16047  * 
16048  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16049  * in the reply previously. 
16050  * 
16051  * <p>
16052  * Example code:
16053  * <pre><code>
16054 var RecordDef = Roo.data.Record.create([
16055     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16056     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16057 ]);
16058 var myReader = new Roo.data.JsonReader({
16059     totalProperty: "results",    // The property which contains the total dataset size (optional)
16060     root: "rows",                // The property which contains an Array of row objects
16061     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16062 }, RecordDef);
16063 </code></pre>
16064  * <p>
16065  * This would consume a JSON file like this:
16066  * <pre><code>
16067 { 'results': 2, 'rows': [
16068     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16069     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16070 }
16071 </code></pre>
16072  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16073  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16074  * paged from the remote server.
16075  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16076  * @cfg {String} root name of the property which contains the Array of row objects.
16077  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16078  * @cfg {Array} fields Array of field definition objects
16079  * @constructor
16080  * Create a new JsonReader
16081  * @param {Object} meta Metadata configuration options
16082  * @param {Object} recordType Either an Array of field definition objects,
16083  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16084  */
16085 Roo.data.JsonReader = function(meta, recordType){
16086     
16087     meta = meta || {};
16088     // set some defaults:
16089     Roo.applyIf(meta, {
16090         totalProperty: 'total',
16091         successProperty : 'success',
16092         root : 'data',
16093         id : 'id'
16094     });
16095     
16096     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16097 };
16098 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16099     
16100     readerType : 'Json',
16101     
16102     /**
16103      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16104      * Used by Store query builder to append _requestMeta to params.
16105      * 
16106      */
16107     metaFromRemote : false,
16108     /**
16109      * This method is only used by a DataProxy which has retrieved data from a remote server.
16110      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16111      * @return {Object} data A data block which is used by an Roo.data.Store object as
16112      * a cache of Roo.data.Records.
16113      */
16114     read : function(response){
16115         var json = response.responseText;
16116        
16117         var o = /* eval:var:o */ eval("("+json+")");
16118         if(!o) {
16119             throw {message: "JsonReader.read: Json object not found"};
16120         }
16121         
16122         if(o.metaData){
16123             
16124             delete this.ef;
16125             this.metaFromRemote = true;
16126             this.meta = o.metaData;
16127             this.recordType = Roo.data.Record.create(o.metaData.fields);
16128             this.onMetaChange(this.meta, this.recordType, o);
16129         }
16130         return this.readRecords(o);
16131     },
16132
16133     // private function a store will implement
16134     onMetaChange : function(meta, recordType, o){
16135
16136     },
16137
16138     /**
16139          * @ignore
16140          */
16141     simpleAccess: function(obj, subsc) {
16142         return obj[subsc];
16143     },
16144
16145         /**
16146          * @ignore
16147          */
16148     getJsonAccessor: function(){
16149         var re = /[\[\.]/;
16150         return function(expr) {
16151             try {
16152                 return(re.test(expr))
16153                     ? new Function("obj", "return obj." + expr)
16154                     : function(obj){
16155                         return obj[expr];
16156                     };
16157             } catch(e){}
16158             return Roo.emptyFn;
16159         };
16160     }(),
16161
16162     /**
16163      * Create a data block containing Roo.data.Records from an XML document.
16164      * @param {Object} o An object which contains an Array of row objects in the property specified
16165      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16166      * which contains the total size of the dataset.
16167      * @return {Object} data A data block which is used by an Roo.data.Store object as
16168      * a cache of Roo.data.Records.
16169      */
16170     readRecords : function(o){
16171         /**
16172          * After any data loads, the raw JSON data is available for further custom processing.
16173          * @type Object
16174          */
16175         this.o = o;
16176         var s = this.meta, Record = this.recordType,
16177             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16178
16179 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16180         if (!this.ef) {
16181             if(s.totalProperty) {
16182                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16183                 }
16184                 if(s.successProperty) {
16185                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16186                 }
16187                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16188                 if (s.id) {
16189                         var g = this.getJsonAccessor(s.id);
16190                         this.getId = function(rec) {
16191                                 var r = g(rec);  
16192                                 return (r === undefined || r === "") ? null : r;
16193                         };
16194                 } else {
16195                         this.getId = function(){return null;};
16196                 }
16197             this.ef = [];
16198             for(var jj = 0; jj < fl; jj++){
16199                 f = fi[jj];
16200                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16201                 this.ef[jj] = this.getJsonAccessor(map);
16202             }
16203         }
16204
16205         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16206         if(s.totalProperty){
16207             var vt = parseInt(this.getTotal(o), 10);
16208             if(!isNaN(vt)){
16209                 totalRecords = vt;
16210             }
16211         }
16212         if(s.successProperty){
16213             var vs = this.getSuccess(o);
16214             if(vs === false || vs === 'false'){
16215                 success = false;
16216             }
16217         }
16218         var records = [];
16219         for(var i = 0; i < c; i++){
16220                 var n = root[i];
16221             var values = {};
16222             var id = this.getId(n);
16223             for(var j = 0; j < fl; j++){
16224                 f = fi[j];
16225             var v = this.ef[j](n);
16226             if (!f.convert) {
16227                 Roo.log('missing convert for ' + f.name);
16228                 Roo.log(f);
16229                 continue;
16230             }
16231             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16232             }
16233             var record = new Record(values, id);
16234             record.json = n;
16235             records[i] = record;
16236         }
16237         return {
16238             raw : o,
16239             success : success,
16240             records : records,
16241             totalRecords : totalRecords
16242         };
16243     },
16244     // used when loading children.. @see loadDataFromChildren
16245     toLoadData: function(rec)
16246     {
16247         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16248         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16249         return { data : data, total : data.length };
16250         
16251     }
16252 });/*
16253  * Based on:
16254  * Ext JS Library 1.1.1
16255  * Copyright(c) 2006-2007, Ext JS, LLC.
16256  *
16257  * Originally Released Under LGPL - original licence link has changed is not relivant.
16258  *
16259  * Fork - LGPL
16260  * <script type="text/javascript">
16261  */
16262
16263 /**
16264  * @class Roo.data.ArrayReader
16265  * @extends Roo.data.DataReader
16266  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16267  * Each element of that Array represents a row of data fields. The
16268  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16269  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16270  * <p>
16271  * Example code:.
16272  * <pre><code>
16273 var RecordDef = Roo.data.Record.create([
16274     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16275     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16276 ]);
16277 var myReader = new Roo.data.ArrayReader({
16278     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16279 }, RecordDef);
16280 </code></pre>
16281  * <p>
16282  * This would consume an Array like this:
16283  * <pre><code>
16284 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16285   </code></pre>
16286  
16287  * @constructor
16288  * Create a new JsonReader
16289  * @param {Object} meta Metadata configuration options.
16290  * @param {Object|Array} recordType Either an Array of field definition objects
16291  * 
16292  * @cfg {Array} fields Array of field definition objects
16293  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16294  * as specified to {@link Roo.data.Record#create},
16295  * or an {@link Roo.data.Record} object
16296  *
16297  * 
16298  * created using {@link Roo.data.Record#create}.
16299  */
16300 Roo.data.ArrayReader = function(meta, recordType)
16301 {    
16302     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16303 };
16304
16305 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16306     
16307       /**
16308      * Create a data block containing Roo.data.Records from an XML document.
16309      * @param {Object} o An Array of row objects which represents the dataset.
16310      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16311      * a cache of Roo.data.Records.
16312      */
16313     readRecords : function(o)
16314     {
16315         var sid = this.meta ? this.meta.id : null;
16316         var recordType = this.recordType, fields = recordType.prototype.fields;
16317         var records = [];
16318         var root = o;
16319         for(var i = 0; i < root.length; i++){
16320             var n = root[i];
16321             var values = {};
16322             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16323             for(var j = 0, jlen = fields.length; j < jlen; j++){
16324                 var f = fields.items[j];
16325                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16326                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16327                 v = f.convert(v);
16328                 values[f.name] = v;
16329             }
16330             var record = new recordType(values, id);
16331             record.json = n;
16332             records[records.length] = record;
16333         }
16334         return {
16335             records : records,
16336             totalRecords : records.length
16337         };
16338     },
16339     // used when loading children.. @see loadDataFromChildren
16340     toLoadData: function(rec)
16341     {
16342         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16343         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16344         
16345     }
16346     
16347     
16348 });/*
16349  * - LGPL
16350  * * 
16351  */
16352
16353 /**
16354  * @class Roo.bootstrap.ComboBox
16355  * @extends Roo.bootstrap.TriggerField
16356  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16357  * @cfg {Boolean} append (true|false) default false
16358  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16359  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16360  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16361  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16362  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16363  * @cfg {Boolean} animate default true
16364  * @cfg {Boolean} emptyResultText only for touch device
16365  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16366  * @cfg {String} emptyTitle default ''
16367  * @cfg {Number} width fixed with? experimental
16368  * @constructor
16369  * Create a new ComboBox.
16370  * @param {Object} config Configuration options
16371  */
16372 Roo.bootstrap.ComboBox = function(config){
16373     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16374     this.addEvents({
16375         /**
16376          * @event expand
16377          * Fires when the dropdown list is expanded
16378         * @param {Roo.bootstrap.ComboBox} combo This combo box
16379         */
16380         'expand' : true,
16381         /**
16382          * @event collapse
16383          * Fires when the dropdown list is collapsed
16384         * @param {Roo.bootstrap.ComboBox} combo This combo box
16385         */
16386         'collapse' : true,
16387         /**
16388          * @event beforeselect
16389          * Fires before a list item is selected. Return false to cancel the selection.
16390         * @param {Roo.bootstrap.ComboBox} combo This combo box
16391         * @param {Roo.data.Record} record The data record returned from the underlying store
16392         * @param {Number} index The index of the selected item in the dropdown list
16393         */
16394         'beforeselect' : true,
16395         /**
16396          * @event select
16397          * Fires when a list item is selected
16398         * @param {Roo.bootstrap.ComboBox} combo This combo box
16399         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16400         * @param {Number} index The index of the selected item in the dropdown list
16401         */
16402         'select' : true,
16403         /**
16404          * @event beforequery
16405          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16406          * The event object passed has these properties:
16407         * @param {Roo.bootstrap.ComboBox} combo This combo box
16408         * @param {String} query The query
16409         * @param {Boolean} forceAll true to force "all" query
16410         * @param {Boolean} cancel true to cancel the query
16411         * @param {Object} e The query event object
16412         */
16413         'beforequery': true,
16414          /**
16415          * @event add
16416          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16417         * @param {Roo.bootstrap.ComboBox} combo This combo box
16418         */
16419         'add' : true,
16420         /**
16421          * @event edit
16422          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16423         * @param {Roo.bootstrap.ComboBox} combo This combo box
16424         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16425         */
16426         'edit' : true,
16427         /**
16428          * @event remove
16429          * Fires when the remove value from the combobox array
16430         * @param {Roo.bootstrap.ComboBox} combo This combo box
16431         */
16432         'remove' : true,
16433         /**
16434          * @event afterremove
16435          * Fires when the remove value from the combobox array
16436         * @param {Roo.bootstrap.ComboBox} combo This combo box
16437         */
16438         'afterremove' : true,
16439         /**
16440          * @event specialfilter
16441          * Fires when specialfilter
16442             * @param {Roo.bootstrap.ComboBox} combo This combo box
16443             */
16444         'specialfilter' : true,
16445         /**
16446          * @event tick
16447          * Fires when tick the element
16448             * @param {Roo.bootstrap.ComboBox} combo This combo box
16449             */
16450         'tick' : true,
16451         /**
16452          * @event touchviewdisplay
16453          * Fires when touch view require special display (default is using displayField)
16454             * @param {Roo.bootstrap.ComboBox} combo This combo box
16455             * @param {Object} cfg set html .
16456             */
16457         'touchviewdisplay' : true
16458         
16459     });
16460     
16461     this.item = [];
16462     this.tickItems = [];
16463     
16464     this.selectedIndex = -1;
16465     if(this.mode == 'local'){
16466         if(config.queryDelay === undefined){
16467             this.queryDelay = 10;
16468         }
16469         if(config.minChars === undefined){
16470             this.minChars = 0;
16471         }
16472     }
16473 };
16474
16475 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16476      
16477     /**
16478      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16479      * rendering into an Roo.Editor, defaults to false)
16480      */
16481     /**
16482      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16483      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16484      */
16485     /**
16486      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16487      */
16488     /**
16489      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16490      * the dropdown list (defaults to undefined, with no header element)
16491      */
16492
16493      /**
16494      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16495      */
16496      
16497      /**
16498      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16499      */
16500     listWidth: undefined,
16501     /**
16502      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16503      * mode = 'remote' or 'text' if mode = 'local')
16504      */
16505     displayField: undefined,
16506     
16507     /**
16508      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16509      * mode = 'remote' or 'value' if mode = 'local'). 
16510      * Note: use of a valueField requires the user make a selection
16511      * in order for a value to be mapped.
16512      */
16513     valueField: undefined,
16514     /**
16515      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16516      */
16517     modalTitle : '',
16518     
16519     /**
16520      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16521      * field's data value (defaults to the underlying DOM element's name)
16522      */
16523     hiddenName: undefined,
16524     /**
16525      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16526      */
16527     listClass: '',
16528     /**
16529      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16530      */
16531     selectedClass: 'active',
16532     
16533     /**
16534      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16535      */
16536     shadow:'sides',
16537     /**
16538      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16539      * anchor positions (defaults to 'tl-bl')
16540      */
16541     listAlign: 'tl-bl?',
16542     /**
16543      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16544      */
16545     maxHeight: 300,
16546     /**
16547      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16548      * query specified by the allQuery config option (defaults to 'query')
16549      */
16550     triggerAction: 'query',
16551     /**
16552      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16553      * (defaults to 4, does not apply if editable = false)
16554      */
16555     minChars : 4,
16556     /**
16557      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16558      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16559      */
16560     typeAhead: false,
16561     /**
16562      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16563      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16564      */
16565     queryDelay: 500,
16566     /**
16567      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16568      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16569      */
16570     pageSize: 0,
16571     /**
16572      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16573      * when editable = true (defaults to false)
16574      */
16575     selectOnFocus:false,
16576     /**
16577      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16578      */
16579     queryParam: 'query',
16580     /**
16581      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16582      * when mode = 'remote' (defaults to 'Loading...')
16583      */
16584     loadingText: 'Loading...',
16585     /**
16586      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16587      */
16588     resizable: false,
16589     /**
16590      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16591      */
16592     handleHeight : 8,
16593     /**
16594      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16595      * traditional select (defaults to true)
16596      */
16597     editable: true,
16598     /**
16599      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16600      */
16601     allQuery: '',
16602     /**
16603      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16604      */
16605     mode: 'remote',
16606     /**
16607      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16608      * listWidth has a higher value)
16609      */
16610     minListWidth : 70,
16611     /**
16612      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16613      * allow the user to set arbitrary text into the field (defaults to false)
16614      */
16615     forceSelection:false,
16616     /**
16617      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16618      * if typeAhead = true (defaults to 250)
16619      */
16620     typeAheadDelay : 250,
16621     /**
16622      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16623      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16624      */
16625     valueNotFoundText : undefined,
16626     /**
16627      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16628      */
16629     blockFocus : false,
16630     
16631     /**
16632      * @cfg {Boolean} disableClear Disable showing of clear button.
16633      */
16634     disableClear : false,
16635     /**
16636      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16637      */
16638     alwaysQuery : false,
16639     
16640     /**
16641      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16642      */
16643     multiple : false,
16644     
16645     /**
16646      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16647      */
16648     invalidClass : "has-warning",
16649     
16650     /**
16651      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16652      */
16653     validClass : "has-success",
16654     
16655     /**
16656      * @cfg {Boolean} specialFilter (true|false) special filter default false
16657      */
16658     specialFilter : false,
16659     
16660     /**
16661      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16662      */
16663     mobileTouchView : true,
16664     
16665     /**
16666      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16667      */
16668     useNativeIOS : false,
16669     
16670     /**
16671      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16672      */
16673     mobile_restrict_height : false,
16674     
16675     ios_options : false,
16676     
16677     //private
16678     addicon : false,
16679     editicon: false,
16680     
16681     page: 0,
16682     hasQuery: false,
16683     append: false,
16684     loadNext: false,
16685     autoFocus : true,
16686     tickable : false,
16687     btnPosition : 'right',
16688     triggerList : true,
16689     showToggleBtn : true,
16690     animate : true,
16691     emptyResultText: 'Empty',
16692     triggerText : 'Select',
16693     emptyTitle : '',
16694     width : false,
16695     
16696     // element that contains real text value.. (when hidden is used..)
16697     
16698     getAutoCreate : function()
16699     {   
16700         var cfg = false;
16701         //render
16702         /*
16703          * Render classic select for iso
16704          */
16705         
16706         if(Roo.isIOS && this.useNativeIOS){
16707             cfg = this.getAutoCreateNativeIOS();
16708             return cfg;
16709         }
16710         
16711         /*
16712          * Touch Devices
16713          */
16714         
16715         if(Roo.isTouch && this.mobileTouchView){
16716             cfg = this.getAutoCreateTouchView();
16717             return cfg;;
16718         }
16719         
16720         /*
16721          *  Normal ComboBox
16722          */
16723         if(!this.tickable){
16724             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16725             return cfg;
16726         }
16727         
16728         /*
16729          *  ComboBox with tickable selections
16730          */
16731              
16732         var align = this.labelAlign || this.parentLabelAlign();
16733         
16734         cfg = {
16735             cls : 'form-group roo-combobox-tickable' //input-group
16736         };
16737         
16738         var btn_text_select = '';
16739         var btn_text_done = '';
16740         var btn_text_cancel = '';
16741         
16742         if (this.btn_text_show) {
16743             btn_text_select = 'Select';
16744             btn_text_done = 'Done';
16745             btn_text_cancel = 'Cancel'; 
16746         }
16747         
16748         var buttons = {
16749             tag : 'div',
16750             cls : 'tickable-buttons',
16751             cn : [
16752                 {
16753                     tag : 'button',
16754                     type : 'button',
16755                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16756                     //html : this.triggerText
16757                     html: btn_text_select
16758                 },
16759                 {
16760                     tag : 'button',
16761                     type : 'button',
16762                     name : 'ok',
16763                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16764                     //html : 'Done'
16765                     html: btn_text_done
16766                 },
16767                 {
16768                     tag : 'button',
16769                     type : 'button',
16770                     name : 'cancel',
16771                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16772                     //html : 'Cancel'
16773                     html: btn_text_cancel
16774                 }
16775             ]
16776         };
16777         
16778         if(this.editable){
16779             buttons.cn.unshift({
16780                 tag: 'input',
16781                 cls: 'roo-select2-search-field-input'
16782             });
16783         }
16784         
16785         var _this = this;
16786         
16787         Roo.each(buttons.cn, function(c){
16788             if (_this.size) {
16789                 c.cls += ' btn-' + _this.size;
16790             }
16791
16792             if (_this.disabled) {
16793                 c.disabled = true;
16794             }
16795         });
16796         
16797         var box = {
16798             tag: 'div',
16799             style : 'display: contents',
16800             cn: [
16801                 {
16802                     tag: 'input',
16803                     type : 'hidden',
16804                     cls: 'form-hidden-field'
16805                 },
16806                 {
16807                     tag: 'ul',
16808                     cls: 'roo-select2-choices',
16809                     cn:[
16810                         {
16811                             tag: 'li',
16812                             cls: 'roo-select2-search-field',
16813                             cn: [
16814                                 buttons
16815                             ]
16816                         }
16817                     ]
16818                 }
16819             ]
16820         };
16821         
16822         var combobox = {
16823             cls: 'roo-select2-container input-group roo-select2-container-multi',
16824             cn: [
16825                 
16826                 box
16827 //                {
16828 //                    tag: 'ul',
16829 //                    cls: 'typeahead typeahead-long dropdown-menu',
16830 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16831 //                }
16832             ]
16833         };
16834         
16835         if(this.hasFeedback && !this.allowBlank){
16836             
16837             var feedback = {
16838                 tag: 'span',
16839                 cls: 'glyphicon form-control-feedback'
16840             };
16841
16842             combobox.cn.push(feedback);
16843         }
16844         
16845         
16846         
16847         var indicator = {
16848             tag : 'i',
16849             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16850             tooltip : 'This field is required'
16851         };
16852         if (Roo.bootstrap.version == 4) {
16853             indicator = {
16854                 tag : 'i',
16855                 style : 'display:none'
16856             };
16857         }
16858         if (align ==='left' && this.fieldLabel.length) {
16859             
16860             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16861             
16862             cfg.cn = [
16863                 indicator,
16864                 {
16865                     tag: 'label',
16866                     'for' :  id,
16867                     cls : 'control-label col-form-label',
16868                     html : this.fieldLabel
16869
16870                 },
16871                 {
16872                     cls : "", 
16873                     cn: [
16874                         combobox
16875                     ]
16876                 }
16877
16878             ];
16879             
16880             var labelCfg = cfg.cn[1];
16881             var contentCfg = cfg.cn[2];
16882             
16883
16884             if(this.indicatorpos == 'right'){
16885                 
16886                 cfg.cn = [
16887                     {
16888                         tag: 'label',
16889                         'for' :  id,
16890                         cls : 'control-label col-form-label',
16891                         cn : [
16892                             {
16893                                 tag : 'span',
16894                                 html : this.fieldLabel
16895                             },
16896                             indicator
16897                         ]
16898                     },
16899                     {
16900                         cls : "",
16901                         cn: [
16902                             combobox
16903                         ]
16904                     }
16905
16906                 ];
16907                 
16908                 
16909                 
16910                 labelCfg = cfg.cn[0];
16911                 contentCfg = cfg.cn[1];
16912             
16913             }
16914             
16915             if(this.labelWidth > 12){
16916                 labelCfg.style = "width: " + this.labelWidth + 'px';
16917             }
16918             if(this.width * 1 > 0){
16919                 contentCfg.style = "width: " + this.width + 'px';
16920             }
16921             if(this.labelWidth < 13 && this.labelmd == 0){
16922                 this.labelmd = this.labelWidth;
16923             }
16924             
16925             if(this.labellg > 0){
16926                 labelCfg.cls += ' col-lg-' + this.labellg;
16927                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16928             }
16929             
16930             if(this.labelmd > 0){
16931                 labelCfg.cls += ' col-md-' + this.labelmd;
16932                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16933             }
16934             
16935             if(this.labelsm > 0){
16936                 labelCfg.cls += ' col-sm-' + this.labelsm;
16937                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16938             }
16939             
16940             if(this.labelxs > 0){
16941                 labelCfg.cls += ' col-xs-' + this.labelxs;
16942                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16943             }
16944                 
16945                 
16946         } else if ( this.fieldLabel.length) {
16947 //                Roo.log(" label");
16948                  cfg.cn = [
16949                    indicator,
16950                     {
16951                         tag: 'label',
16952                         //cls : 'input-group-addon',
16953                         html : this.fieldLabel
16954                     },
16955                     combobox
16956                 ];
16957                 
16958                 if(this.indicatorpos == 'right'){
16959                     cfg.cn = [
16960                         {
16961                             tag: 'label',
16962                             //cls : 'input-group-addon',
16963                             html : this.fieldLabel
16964                         },
16965                         indicator,
16966                         combobox
16967                     ];
16968                     
16969                 }
16970
16971         } else {
16972             
16973 //                Roo.log(" no label && no align");
16974                 cfg = combobox
16975                      
16976                 
16977         }
16978          
16979         var settings=this;
16980         ['xs','sm','md','lg'].map(function(size){
16981             if (settings[size]) {
16982                 cfg.cls += ' col-' + size + '-' + settings[size];
16983             }
16984         });
16985         
16986         return cfg;
16987         
16988     },
16989     
16990     _initEventsCalled : false,
16991     
16992     // private
16993     initEvents: function()
16994     {   
16995         if (this._initEventsCalled) { // as we call render... prevent looping...
16996             return;
16997         }
16998         this._initEventsCalled = true;
16999         
17000         if (!this.store) {
17001             throw "can not find store for combo";
17002         }
17003         
17004         this.indicator = this.indicatorEl();
17005         
17006         this.store = Roo.factory(this.store, Roo.data);
17007         this.store.parent = this;
17008         
17009         // if we are building from html. then this element is so complex, that we can not really
17010         // use the rendered HTML.
17011         // so we have to trash and replace the previous code.
17012         if (Roo.XComponent.build_from_html) {
17013             // remove this element....
17014             var e = this.el.dom, k=0;
17015             while (e ) { e = e.previousSibling;  ++k;}
17016
17017             this.el.remove();
17018             
17019             this.el=false;
17020             this.rendered = false;
17021             
17022             this.render(this.parent().getChildContainer(true), k);
17023         }
17024         
17025         if(Roo.isIOS && this.useNativeIOS){
17026             this.initIOSView();
17027             return;
17028         }
17029         
17030         /*
17031          * Touch Devices
17032          */
17033         
17034         if(Roo.isTouch && this.mobileTouchView){
17035             this.initTouchView();
17036             return;
17037         }
17038         
17039         if(this.tickable){
17040             this.initTickableEvents();
17041             return;
17042         }
17043         
17044         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17045         
17046         if(this.hiddenName){
17047             
17048             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17049             
17050             this.hiddenField.dom.value =
17051                 this.hiddenValue !== undefined ? this.hiddenValue :
17052                 this.value !== undefined ? this.value : '';
17053
17054             // prevent input submission
17055             this.el.dom.removeAttribute('name');
17056             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17057              
17058              
17059         }
17060         //if(Roo.isGecko){
17061         //    this.el.dom.setAttribute('autocomplete', 'off');
17062         //}
17063         
17064         var cls = 'x-combo-list';
17065         
17066         //this.list = new Roo.Layer({
17067         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17068         //});
17069         
17070         var _this = this;
17071         
17072         (function(){
17073             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17074             _this.list.setWidth(lw);
17075         }).defer(100);
17076         
17077         this.list.on('mouseover', this.onViewOver, this);
17078         this.list.on('mousemove', this.onViewMove, this);
17079         this.list.on('scroll', this.onViewScroll, this);
17080         
17081         /*
17082         this.list.swallowEvent('mousewheel');
17083         this.assetHeight = 0;
17084
17085         if(this.title){
17086             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17087             this.assetHeight += this.header.getHeight();
17088         }
17089
17090         this.innerList = this.list.createChild({cls:cls+'-inner'});
17091         this.innerList.on('mouseover', this.onViewOver, this);
17092         this.innerList.on('mousemove', this.onViewMove, this);
17093         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17094         
17095         if(this.allowBlank && !this.pageSize && !this.disableClear){
17096             this.footer = this.list.createChild({cls:cls+'-ft'});
17097             this.pageTb = new Roo.Toolbar(this.footer);
17098            
17099         }
17100         if(this.pageSize){
17101             this.footer = this.list.createChild({cls:cls+'-ft'});
17102             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17103                     {pageSize: this.pageSize});
17104             
17105         }
17106         
17107         if (this.pageTb && this.allowBlank && !this.disableClear) {
17108             var _this = this;
17109             this.pageTb.add(new Roo.Toolbar.Fill(), {
17110                 cls: 'x-btn-icon x-btn-clear',
17111                 text: '&#160;',
17112                 handler: function()
17113                 {
17114                     _this.collapse();
17115                     _this.clearValue();
17116                     _this.onSelect(false, -1);
17117                 }
17118             });
17119         }
17120         if (this.footer) {
17121             this.assetHeight += this.footer.getHeight();
17122         }
17123         */
17124             
17125         if(!this.tpl){
17126             this.tpl = Roo.bootstrap.version == 4 ?
17127                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17128                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17129         }
17130
17131         this.view = new Roo.View(this.list, this.tpl, {
17132             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17133         });
17134         //this.view.wrapEl.setDisplayed(false);
17135         this.view.on('click', this.onViewClick, this);
17136         
17137         
17138         this.store.on('beforeload', this.onBeforeLoad, this);
17139         this.store.on('load', this.onLoad, this);
17140         this.store.on('loadexception', this.onLoadException, this);
17141         /*
17142         if(this.resizable){
17143             this.resizer = new Roo.Resizable(this.list,  {
17144                pinned:true, handles:'se'
17145             });
17146             this.resizer.on('resize', function(r, w, h){
17147                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17148                 this.listWidth = w;
17149                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17150                 this.restrictHeight();
17151             }, this);
17152             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17153         }
17154         */
17155         if(!this.editable){
17156             this.editable = true;
17157             this.setEditable(false);
17158         }
17159         
17160         /*
17161         
17162         if (typeof(this.events.add.listeners) != 'undefined') {
17163             
17164             this.addicon = this.wrap.createChild(
17165                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17166        
17167             this.addicon.on('click', function(e) {
17168                 this.fireEvent('add', this);
17169             }, this);
17170         }
17171         if (typeof(this.events.edit.listeners) != 'undefined') {
17172             
17173             this.editicon = this.wrap.createChild(
17174                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17175             if (this.addicon) {
17176                 this.editicon.setStyle('margin-left', '40px');
17177             }
17178             this.editicon.on('click', function(e) {
17179                 
17180                 // we fire even  if inothing is selected..
17181                 this.fireEvent('edit', this, this.lastData );
17182                 
17183             }, this);
17184         }
17185         */
17186         
17187         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17188             "up" : function(e){
17189                 this.inKeyMode = true;
17190                 this.selectPrev();
17191             },
17192
17193             "down" : function(e){
17194                 if(!this.isExpanded()){
17195                     this.onTriggerClick();
17196                 }else{
17197                     this.inKeyMode = true;
17198                     this.selectNext();
17199                 }
17200             },
17201
17202             "enter" : function(e){
17203 //                this.onViewClick();
17204                 //return true;
17205                 this.collapse();
17206                 
17207                 if(this.fireEvent("specialkey", this, e)){
17208                     this.onViewClick(false);
17209                 }
17210                 
17211                 return true;
17212             },
17213
17214             "esc" : function(e){
17215                 this.collapse();
17216             },
17217
17218             "tab" : function(e){
17219                 this.collapse();
17220                 
17221                 if(this.fireEvent("specialkey", this, e)){
17222                     this.onViewClick(false);
17223                 }
17224                 
17225                 return true;
17226             },
17227
17228             scope : this,
17229
17230             doRelay : function(foo, bar, hname){
17231                 if(hname == 'down' || this.scope.isExpanded()){
17232                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17233                 }
17234                 return true;
17235             },
17236
17237             forceKeyDown: true
17238         });
17239         
17240         
17241         this.queryDelay = Math.max(this.queryDelay || 10,
17242                 this.mode == 'local' ? 10 : 250);
17243         
17244         
17245         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17246         
17247         if(this.typeAhead){
17248             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17249         }
17250         if(this.editable !== false){
17251             this.inputEl().on("keyup", this.onKeyUp, this);
17252         }
17253         if(this.forceSelection){
17254             this.inputEl().on('blur', this.doForce, this);
17255         }
17256         
17257         if(this.multiple){
17258             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17259             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17260         }
17261     },
17262     
17263     initTickableEvents: function()
17264     {   
17265         this.createList();
17266         
17267         if(this.hiddenName){
17268             
17269             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17270             
17271             this.hiddenField.dom.value =
17272                 this.hiddenValue !== undefined ? this.hiddenValue :
17273                 this.value !== undefined ? this.value : '';
17274
17275             // prevent input submission
17276             this.el.dom.removeAttribute('name');
17277             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17278              
17279              
17280         }
17281         
17282 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17283         
17284         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17285         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17286         if(this.triggerList){
17287             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17288         }
17289          
17290         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17291         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17292         
17293         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17294         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17295         
17296         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17297         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17298         
17299         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17300         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17301         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17302         
17303         this.okBtn.hide();
17304         this.cancelBtn.hide();
17305         
17306         var _this = this;
17307         
17308         (function(){
17309             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17310             _this.list.setWidth(lw);
17311         }).defer(100);
17312         
17313         this.list.on('mouseover', this.onViewOver, this);
17314         this.list.on('mousemove', this.onViewMove, this);
17315         
17316         this.list.on('scroll', this.onViewScroll, this);
17317         
17318         if(!this.tpl){
17319             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17320                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17321         }
17322
17323         this.view = new Roo.View(this.list, this.tpl, {
17324             singleSelect:true,
17325             tickable:true,
17326             parent:this,
17327             store: this.store,
17328             selectedClass: this.selectedClass
17329         });
17330         
17331         //this.view.wrapEl.setDisplayed(false);
17332         this.view.on('click', this.onViewClick, this);
17333         
17334         
17335         
17336         this.store.on('beforeload', this.onBeforeLoad, this);
17337         this.store.on('load', this.onLoad, this);
17338         this.store.on('loadexception', this.onLoadException, this);
17339         
17340         if(this.editable){
17341             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17342                 "up" : function(e){
17343                     this.inKeyMode = true;
17344                     this.selectPrev();
17345                 },
17346
17347                 "down" : function(e){
17348                     this.inKeyMode = true;
17349                     this.selectNext();
17350                 },
17351
17352                 "enter" : function(e){
17353                     if(this.fireEvent("specialkey", this, e)){
17354                         this.onViewClick(false);
17355                     }
17356                     
17357                     return true;
17358                 },
17359
17360                 "esc" : function(e){
17361                     this.onTickableFooterButtonClick(e, false, false);
17362                 },
17363
17364                 "tab" : function(e){
17365                     this.fireEvent("specialkey", this, e);
17366                     
17367                     this.onTickableFooterButtonClick(e, false, false);
17368                     
17369                     return true;
17370                 },
17371
17372                 scope : this,
17373
17374                 doRelay : function(e, fn, key){
17375                     if(this.scope.isExpanded()){
17376                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17377                     }
17378                     return true;
17379                 },
17380
17381                 forceKeyDown: true
17382             });
17383         }
17384         
17385         this.queryDelay = Math.max(this.queryDelay || 10,
17386                 this.mode == 'local' ? 10 : 250);
17387         
17388         
17389         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17390         
17391         if(this.typeAhead){
17392             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17393         }
17394         
17395         if(this.editable !== false){
17396             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17397         }
17398         
17399         this.indicator = this.indicatorEl();
17400         
17401         if(this.indicator){
17402             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17403             this.indicator.hide();
17404         }
17405         
17406     },
17407
17408     onDestroy : function(){
17409         if(this.view){
17410             this.view.setStore(null);
17411             this.view.el.removeAllListeners();
17412             this.view.el.remove();
17413             this.view.purgeListeners();
17414         }
17415         if(this.list){
17416             this.list.dom.innerHTML  = '';
17417         }
17418         
17419         if(this.store){
17420             this.store.un('beforeload', this.onBeforeLoad, this);
17421             this.store.un('load', this.onLoad, this);
17422             this.store.un('loadexception', this.onLoadException, this);
17423         }
17424         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17425     },
17426
17427     // private
17428     fireKey : function(e){
17429         if(e.isNavKeyPress() && !this.list.isVisible()){
17430             this.fireEvent("specialkey", this, e);
17431         }
17432     },
17433
17434     // private
17435     onResize: function(w, h)
17436     {
17437         
17438         
17439 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17440 //        
17441 //        if(typeof w != 'number'){
17442 //            // we do not handle it!?!?
17443 //            return;
17444 //        }
17445 //        var tw = this.trigger.getWidth();
17446 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17447 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17448 //        var x = w - tw;
17449 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17450 //            
17451 //        //this.trigger.setStyle('left', x+'px');
17452 //        
17453 //        if(this.list && this.listWidth === undefined){
17454 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17455 //            this.list.setWidth(lw);
17456 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17457 //        }
17458         
17459     
17460         
17461     },
17462
17463     /**
17464      * Allow or prevent the user from directly editing the field text.  If false is passed,
17465      * the user will only be able to select from the items defined in the dropdown list.  This method
17466      * is the runtime equivalent of setting the 'editable' config option at config time.
17467      * @param {Boolean} value True to allow the user to directly edit the field text
17468      */
17469     setEditable : function(value){
17470         if(value == this.editable){
17471             return;
17472         }
17473         this.editable = value;
17474         if(!value){
17475             this.inputEl().dom.setAttribute('readOnly', true);
17476             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17477             this.inputEl().addClass('x-combo-noedit');
17478         }else{
17479             this.inputEl().dom.removeAttribute('readOnly');
17480             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17481             this.inputEl().removeClass('x-combo-noedit');
17482         }
17483     },
17484
17485     // private
17486     
17487     onBeforeLoad : function(combo,opts){
17488         if(!this.hasFocus){
17489             return;
17490         }
17491          if (!opts.add) {
17492             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17493          }
17494         this.restrictHeight();
17495         this.selectedIndex = -1;
17496     },
17497
17498     // private
17499     onLoad : function(){
17500         
17501         this.hasQuery = false;
17502         
17503         if(!this.hasFocus){
17504             return;
17505         }
17506         
17507         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17508             this.loading.hide();
17509         }
17510         
17511         if(this.store.getCount() > 0){
17512             
17513             this.expand();
17514             this.restrictHeight();
17515             if(this.lastQuery == this.allQuery){
17516                 if(this.editable && !this.tickable){
17517                     this.inputEl().dom.select();
17518                 }
17519                 
17520                 if(
17521                     !this.selectByValue(this.value, true) &&
17522                     this.autoFocus && 
17523                     (
17524                         !this.store.lastOptions ||
17525                         typeof(this.store.lastOptions.add) == 'undefined' || 
17526                         this.store.lastOptions.add != true
17527                     )
17528                 ){
17529                     this.select(0, true);
17530                 }
17531             }else{
17532                 if(this.autoFocus){
17533                     this.selectNext();
17534                 }
17535                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17536                     this.taTask.delay(this.typeAheadDelay);
17537                 }
17538             }
17539         }else{
17540             this.onEmptyResults();
17541         }
17542         
17543         //this.el.focus();
17544     },
17545     // private
17546     onLoadException : function()
17547     {
17548         this.hasQuery = false;
17549         
17550         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17551             this.loading.hide();
17552         }
17553         
17554         if(this.tickable && this.editable){
17555             return;
17556         }
17557         
17558         this.collapse();
17559         // only causes errors at present
17560         //Roo.log(this.store.reader.jsonData);
17561         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17562             // fixme
17563             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17564         //}
17565         
17566         
17567     },
17568     // private
17569     onTypeAhead : function(){
17570         if(this.store.getCount() > 0){
17571             var r = this.store.getAt(0);
17572             var newValue = r.data[this.displayField];
17573             var len = newValue.length;
17574             var selStart = this.getRawValue().length;
17575             
17576             if(selStart != len){
17577                 this.setRawValue(newValue);
17578                 this.selectText(selStart, newValue.length);
17579             }
17580         }
17581     },
17582
17583     // private
17584     onSelect : function(record, index){
17585         
17586         if(this.fireEvent('beforeselect', this, record, index) !== false){
17587         
17588             this.setFromData(index > -1 ? record.data : false);
17589             
17590             this.collapse();
17591             this.fireEvent('select', this, record, index);
17592         }
17593     },
17594
17595     /**
17596      * Returns the currently selected field value or empty string if no value is set.
17597      * @return {String} value The selected value
17598      */
17599     getValue : function()
17600     {
17601         if(Roo.isIOS && this.useNativeIOS){
17602             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17603         }
17604         
17605         if(this.multiple){
17606             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17607         }
17608         
17609         if(this.valueField){
17610             return typeof this.value != 'undefined' ? this.value : '';
17611         }else{
17612             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17613         }
17614     },
17615     
17616     getRawValue : function()
17617     {
17618         if(Roo.isIOS && this.useNativeIOS){
17619             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17620         }
17621         
17622         var v = this.inputEl().getValue();
17623         
17624         return v;
17625     },
17626
17627     /**
17628      * Clears any text/value currently set in the field
17629      */
17630     clearValue : function(){
17631         
17632         if(this.hiddenField){
17633             this.hiddenField.dom.value = '';
17634         }
17635         this.value = '';
17636         this.setRawValue('');
17637         this.lastSelectionText = '';
17638         this.lastData = false;
17639         
17640         var close = this.closeTriggerEl();
17641         
17642         if(close){
17643             close.hide();
17644         }
17645         
17646         this.validate();
17647         
17648     },
17649
17650     /**
17651      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17652      * will be displayed in the field.  If the value does not match the data value of an existing item,
17653      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17654      * Otherwise the field will be blank (although the value will still be set).
17655      * @param {String} value The value to match
17656      */
17657     setValue : function(v)
17658     {
17659         if(Roo.isIOS && this.useNativeIOS){
17660             this.setIOSValue(v);
17661             return;
17662         }
17663         
17664         if(this.multiple){
17665             this.syncValue();
17666             return;
17667         }
17668         
17669         var text = v;
17670         if(this.valueField){
17671             var r = this.findRecord(this.valueField, v);
17672             if(r){
17673                 text = r.data[this.displayField];
17674             }else if(this.valueNotFoundText !== undefined){
17675                 text = this.valueNotFoundText;
17676             }
17677         }
17678         this.lastSelectionText = text;
17679         if(this.hiddenField){
17680             this.hiddenField.dom.value = v;
17681         }
17682         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17683         this.value = v;
17684         
17685         var close = this.closeTriggerEl();
17686         
17687         if(close){
17688             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17689         }
17690         
17691         this.validate();
17692     },
17693     /**
17694      * @property {Object} the last set data for the element
17695      */
17696     
17697     lastData : false,
17698     /**
17699      * Sets the value of the field based on a object which is related to the record format for the store.
17700      * @param {Object} value the value to set as. or false on reset?
17701      */
17702     setFromData : function(o){
17703         
17704         if(this.multiple){
17705             this.addItem(o);
17706             return;
17707         }
17708             
17709         var dv = ''; // display value
17710         var vv = ''; // value value..
17711         this.lastData = o;
17712         if (this.displayField) {
17713             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17714         } else {
17715             // this is an error condition!!!
17716             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17717         }
17718         
17719         if(this.valueField){
17720             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17721         }
17722         
17723         var close = this.closeTriggerEl();
17724         
17725         if(close){
17726             if(dv.length || vv * 1 > 0){
17727                 close.show() ;
17728                 this.blockFocus=true;
17729             } else {
17730                 close.hide();
17731             }             
17732         }
17733         
17734         if(this.hiddenField){
17735             this.hiddenField.dom.value = vv;
17736             
17737             this.lastSelectionText = dv;
17738             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17739             this.value = vv;
17740             return;
17741         }
17742         // no hidden field.. - we store the value in 'value', but still display
17743         // display field!!!!
17744         this.lastSelectionText = dv;
17745         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17746         this.value = vv;
17747         
17748         
17749         
17750     },
17751     // private
17752     reset : function(){
17753         // overridden so that last data is reset..
17754         
17755         if(this.multiple){
17756             this.clearItem();
17757             return;
17758         }
17759         
17760         this.setValue(this.originalValue);
17761         //this.clearInvalid();
17762         this.lastData = false;
17763         if (this.view) {
17764             this.view.clearSelections();
17765         }
17766         
17767         this.validate();
17768     },
17769     // private
17770     findRecord : function(prop, value){
17771         var record;
17772         if(this.store.getCount() > 0){
17773             this.store.each(function(r){
17774                 if(r.data[prop] == value){
17775                     record = r;
17776                     return false;
17777                 }
17778                 return true;
17779             });
17780         }
17781         return record;
17782     },
17783     
17784     getName: function()
17785     {
17786         // returns hidden if it's set..
17787         if (!this.rendered) {return ''};
17788         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17789         
17790     },
17791     // private
17792     onViewMove : function(e, t){
17793         this.inKeyMode = false;
17794     },
17795
17796     // private
17797     onViewOver : function(e, t){
17798         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17799             return;
17800         }
17801         var item = this.view.findItemFromChild(t);
17802         
17803         if(item){
17804             var index = this.view.indexOf(item);
17805             this.select(index, false);
17806         }
17807     },
17808
17809     // private
17810     onViewClick : function(view, doFocus, el, e)
17811     {
17812         var index = this.view.getSelectedIndexes()[0];
17813         
17814         var r = this.store.getAt(index);
17815         
17816         if(this.tickable){
17817             
17818             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17819                 return;
17820             }
17821             
17822             var rm = false;
17823             var _this = this;
17824             
17825             Roo.each(this.tickItems, function(v,k){
17826                 
17827                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17828                     Roo.log(v);
17829                     _this.tickItems.splice(k, 1);
17830                     
17831                     if(typeof(e) == 'undefined' && view == false){
17832                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17833                     }
17834                     
17835                     rm = true;
17836                     return;
17837                 }
17838             });
17839             
17840             if(rm){
17841                 return;
17842             }
17843             
17844             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17845                 this.tickItems.push(r.data);
17846             }
17847             
17848             if(typeof(e) == 'undefined' && view == false){
17849                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17850             }
17851                     
17852             return;
17853         }
17854         
17855         if(r){
17856             this.onSelect(r, index);
17857         }
17858         if(doFocus !== false && !this.blockFocus){
17859             this.inputEl().focus();
17860         }
17861     },
17862
17863     // private
17864     restrictHeight : function(){
17865         //this.innerList.dom.style.height = '';
17866         //var inner = this.innerList.dom;
17867         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17868         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17869         //this.list.beginUpdate();
17870         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17871         this.list.alignTo(this.inputEl(), this.listAlign);
17872         this.list.alignTo(this.inputEl(), this.listAlign);
17873         //this.list.endUpdate();
17874     },
17875
17876     // private
17877     onEmptyResults : function(){
17878         
17879         if(this.tickable && this.editable){
17880             this.hasFocus = false;
17881             this.restrictHeight();
17882             return;
17883         }
17884         
17885         this.collapse();
17886     },
17887
17888     /**
17889      * Returns true if the dropdown list is expanded, else false.
17890      */
17891     isExpanded : function(){
17892         return this.list.isVisible();
17893     },
17894
17895     /**
17896      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17897      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17898      * @param {String} value The data value of the item to select
17899      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17900      * selected item if it is not currently in view (defaults to true)
17901      * @return {Boolean} True if the value matched an item in the list, else false
17902      */
17903     selectByValue : function(v, scrollIntoView){
17904         if(v !== undefined && v !== null){
17905             var r = this.findRecord(this.valueField || this.displayField, v);
17906             if(r){
17907                 this.select(this.store.indexOf(r), scrollIntoView);
17908                 return true;
17909             }
17910         }
17911         return false;
17912     },
17913
17914     /**
17915      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17916      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17917      * @param {Number} index The zero-based index of the list item to select
17918      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17919      * selected item if it is not currently in view (defaults to true)
17920      */
17921     select : function(index, scrollIntoView){
17922         this.selectedIndex = index;
17923         this.view.select(index);
17924         if(scrollIntoView !== false){
17925             var el = this.view.getNode(index);
17926             /*
17927              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17928              */
17929             if(el){
17930                 this.list.scrollChildIntoView(el, false);
17931             }
17932         }
17933     },
17934
17935     // private
17936     selectNext : function(){
17937         var ct = this.store.getCount();
17938         if(ct > 0){
17939             if(this.selectedIndex == -1){
17940                 this.select(0);
17941             }else if(this.selectedIndex < ct-1){
17942                 this.select(this.selectedIndex+1);
17943             }
17944         }
17945     },
17946
17947     // private
17948     selectPrev : function(){
17949         var ct = this.store.getCount();
17950         if(ct > 0){
17951             if(this.selectedIndex == -1){
17952                 this.select(0);
17953             }else if(this.selectedIndex != 0){
17954                 this.select(this.selectedIndex-1);
17955             }
17956         }
17957     },
17958
17959     // private
17960     onKeyUp : function(e){
17961         if(this.editable !== false && !e.isSpecialKey()){
17962             this.lastKey = e.getKey();
17963             this.dqTask.delay(this.queryDelay);
17964         }
17965     },
17966
17967     // private
17968     validateBlur : function(){
17969         return !this.list || !this.list.isVisible();   
17970     },
17971
17972     // private
17973     initQuery : function(){
17974         
17975         var v = this.getRawValue();
17976         
17977         if(this.tickable && this.editable){
17978             v = this.tickableInputEl().getValue();
17979         }
17980         
17981         this.doQuery(v);
17982     },
17983
17984     // private
17985     doForce : function(){
17986         if(this.inputEl().dom.value.length > 0){
17987             this.inputEl().dom.value =
17988                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17989              
17990         }
17991     },
17992
17993     /**
17994      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17995      * query allowing the query action to be canceled if needed.
17996      * @param {String} query The SQL query to execute
17997      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17998      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17999      * saved in the current store (defaults to false)
18000      */
18001     doQuery : function(q, forceAll){
18002         
18003         if(q === undefined || q === null){
18004             q = '';
18005         }
18006         var qe = {
18007             query: q,
18008             forceAll: forceAll,
18009             combo: this,
18010             cancel:false
18011         };
18012         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18013             return false;
18014         }
18015         q = qe.query;
18016         
18017         forceAll = qe.forceAll;
18018         if(forceAll === true || (q.length >= this.minChars)){
18019             
18020             this.hasQuery = true;
18021             
18022             if(this.lastQuery != q || this.alwaysQuery){
18023                 this.lastQuery = q;
18024                 if(this.mode == 'local'){
18025                     this.selectedIndex = -1;
18026                     if(forceAll){
18027                         this.store.clearFilter();
18028                     }else{
18029                         
18030                         if(this.specialFilter){
18031                             this.fireEvent('specialfilter', this);
18032                             this.onLoad();
18033                             return;
18034                         }
18035                         
18036                         this.store.filter(this.displayField, q);
18037                     }
18038                     
18039                     this.store.fireEvent("datachanged", this.store);
18040                     
18041                     this.onLoad();
18042                     
18043                     
18044                 }else{
18045                     
18046                     this.store.baseParams[this.queryParam] = q;
18047                     
18048                     var options = {params : this.getParams(q)};
18049                     
18050                     if(this.loadNext){
18051                         options.add = true;
18052                         options.params.start = this.page * this.pageSize;
18053                     }
18054                     
18055                     this.store.load(options);
18056                     
18057                     /*
18058                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18059                      *  we should expand the list on onLoad
18060                      *  so command out it
18061                      */
18062 //                    this.expand();
18063                 }
18064             }else{
18065                 this.selectedIndex = -1;
18066                 this.onLoad();   
18067             }
18068         }
18069         
18070         this.loadNext = false;
18071     },
18072     
18073     // private
18074     getParams : function(q){
18075         var p = {};
18076         //p[this.queryParam] = q;
18077         
18078         if(this.pageSize){
18079             p.start = 0;
18080             p.limit = this.pageSize;
18081         }
18082         return p;
18083     },
18084
18085     /**
18086      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18087      */
18088     collapse : function(){
18089         if(!this.isExpanded()){
18090             return;
18091         }
18092         
18093         this.list.hide();
18094         
18095         this.hasFocus = false;
18096         
18097         if(this.tickable){
18098             this.okBtn.hide();
18099             this.cancelBtn.hide();
18100             this.trigger.show();
18101             
18102             if(this.editable){
18103                 this.tickableInputEl().dom.value = '';
18104                 this.tickableInputEl().blur();
18105             }
18106             
18107         }
18108         
18109         Roo.get(document).un('mousedown', this.collapseIf, this);
18110         Roo.get(document).un('mousewheel', this.collapseIf, this);
18111         if (!this.editable) {
18112             Roo.get(document).un('keydown', this.listKeyPress, this);
18113         }
18114         this.fireEvent('collapse', this);
18115         
18116         this.validate();
18117     },
18118
18119     // private
18120     collapseIf : function(e){
18121         var in_combo  = e.within(this.el);
18122         var in_list =  e.within(this.list);
18123         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18124         
18125         if (in_combo || in_list || is_list) {
18126             //e.stopPropagation();
18127             return;
18128         }
18129         
18130         if(this.tickable){
18131             this.onTickableFooterButtonClick(e, false, false);
18132         }
18133
18134         this.collapse();
18135         
18136     },
18137
18138     /**
18139      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18140      */
18141     expand : function(){
18142        
18143         if(this.isExpanded() || !this.hasFocus){
18144             return;
18145         }
18146         
18147         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18148         this.list.setWidth(lw);
18149         
18150         Roo.log('expand');
18151         
18152         this.list.show();
18153         
18154         this.restrictHeight();
18155         
18156         if(this.tickable){
18157             
18158             this.tickItems = Roo.apply([], this.item);
18159             
18160             this.okBtn.show();
18161             this.cancelBtn.show();
18162             this.trigger.hide();
18163             
18164             if(this.editable){
18165                 this.tickableInputEl().focus();
18166             }
18167             
18168         }
18169         
18170         Roo.get(document).on('mousedown', this.collapseIf, this);
18171         Roo.get(document).on('mousewheel', this.collapseIf, this);
18172         if (!this.editable) {
18173             Roo.get(document).on('keydown', this.listKeyPress, this);
18174         }
18175         
18176         this.fireEvent('expand', this);
18177     },
18178
18179     // private
18180     // Implements the default empty TriggerField.onTriggerClick function
18181     onTriggerClick : function(e)
18182     {
18183         Roo.log('trigger click');
18184         
18185         if(this.disabled || !this.triggerList){
18186             return;
18187         }
18188         
18189         this.page = 0;
18190         this.loadNext = false;
18191         
18192         if(this.isExpanded()){
18193             this.collapse();
18194             if (!this.blockFocus) {
18195                 this.inputEl().focus();
18196             }
18197             
18198         }else {
18199             this.hasFocus = true;
18200             if(this.triggerAction == 'all') {
18201                 this.doQuery(this.allQuery, true);
18202             } else {
18203                 this.doQuery(this.getRawValue());
18204             }
18205             if (!this.blockFocus) {
18206                 this.inputEl().focus();
18207             }
18208         }
18209     },
18210     
18211     onTickableTriggerClick : function(e)
18212     {
18213         if(this.disabled){
18214             return;
18215         }
18216         
18217         this.page = 0;
18218         this.loadNext = false;
18219         this.hasFocus = true;
18220         
18221         if(this.triggerAction == 'all') {
18222             this.doQuery(this.allQuery, true);
18223         } else {
18224             this.doQuery(this.getRawValue());
18225         }
18226     },
18227     
18228     onSearchFieldClick : function(e)
18229     {
18230         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18231             this.onTickableFooterButtonClick(e, false, false);
18232             return;
18233         }
18234         
18235         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18236             return;
18237         }
18238         
18239         this.page = 0;
18240         this.loadNext = false;
18241         this.hasFocus = true;
18242         
18243         if(this.triggerAction == 'all') {
18244             this.doQuery(this.allQuery, true);
18245         } else {
18246             this.doQuery(this.getRawValue());
18247         }
18248     },
18249     
18250     listKeyPress : function(e)
18251     {
18252         //Roo.log('listkeypress');
18253         // scroll to first matching element based on key pres..
18254         if (e.isSpecialKey()) {
18255             return false;
18256         }
18257         var k = String.fromCharCode(e.getKey()).toUpperCase();
18258         //Roo.log(k);
18259         var match  = false;
18260         var csel = this.view.getSelectedNodes();
18261         var cselitem = false;
18262         if (csel.length) {
18263             var ix = this.view.indexOf(csel[0]);
18264             cselitem  = this.store.getAt(ix);
18265             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18266                 cselitem = false;
18267             }
18268             
18269         }
18270         
18271         this.store.each(function(v) { 
18272             if (cselitem) {
18273                 // start at existing selection.
18274                 if (cselitem.id == v.id) {
18275                     cselitem = false;
18276                 }
18277                 return true;
18278             }
18279                 
18280             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18281                 match = this.store.indexOf(v);
18282                 return false;
18283             }
18284             return true;
18285         }, this);
18286         
18287         if (match === false) {
18288             return true; // no more action?
18289         }
18290         // scroll to?
18291         this.view.select(match);
18292         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18293         sn.scrollIntoView(sn.dom.parentNode, false);
18294     },
18295     
18296     onViewScroll : function(e, t){
18297         
18298         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){
18299             return;
18300         }
18301         
18302         this.hasQuery = true;
18303         
18304         this.loading = this.list.select('.loading', true).first();
18305         
18306         if(this.loading === null){
18307             this.list.createChild({
18308                 tag: 'div',
18309                 cls: 'loading roo-select2-more-results roo-select2-active',
18310                 html: 'Loading more results...'
18311             });
18312             
18313             this.loading = this.list.select('.loading', true).first();
18314             
18315             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18316             
18317             this.loading.hide();
18318         }
18319         
18320         this.loading.show();
18321         
18322         var _combo = this;
18323         
18324         this.page++;
18325         this.loadNext = true;
18326         
18327         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18328         
18329         return;
18330     },
18331     
18332     addItem : function(o)
18333     {   
18334         var dv = ''; // display value
18335         
18336         if (this.displayField) {
18337             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18338         } else {
18339             // this is an error condition!!!
18340             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18341         }
18342         
18343         if(!dv.length){
18344             return;
18345         }
18346         
18347         var choice = this.choices.createChild({
18348             tag: 'li',
18349             cls: 'roo-select2-search-choice',
18350             cn: [
18351                 {
18352                     tag: 'div',
18353                     html: dv
18354                 },
18355                 {
18356                     tag: 'a',
18357                     href: '#',
18358                     cls: 'roo-select2-search-choice-close fa fa-times',
18359                     tabindex: '-1'
18360                 }
18361             ]
18362             
18363         }, this.searchField);
18364         
18365         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18366         
18367         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18368         
18369         this.item.push(o);
18370         
18371         this.lastData = o;
18372         
18373         this.syncValue();
18374         
18375         this.inputEl().dom.value = '';
18376         
18377         this.validate();
18378     },
18379     
18380     onRemoveItem : function(e, _self, o)
18381     {
18382         e.preventDefault();
18383         
18384         this.lastItem = Roo.apply([], this.item);
18385         
18386         var index = this.item.indexOf(o.data) * 1;
18387         
18388         if( index < 0){
18389             Roo.log('not this item?!');
18390             return;
18391         }
18392         
18393         this.item.splice(index, 1);
18394         o.item.remove();
18395         
18396         this.syncValue();
18397         
18398         this.fireEvent('remove', this, e);
18399         
18400         this.validate();
18401         
18402     },
18403     
18404     syncValue : function()
18405     {
18406         if(!this.item.length){
18407             this.clearValue();
18408             return;
18409         }
18410             
18411         var value = [];
18412         var _this = this;
18413         Roo.each(this.item, function(i){
18414             if(_this.valueField){
18415                 value.push(i[_this.valueField]);
18416                 return;
18417             }
18418
18419             value.push(i);
18420         });
18421
18422         this.value = value.join(',');
18423
18424         if(this.hiddenField){
18425             this.hiddenField.dom.value = this.value;
18426         }
18427         
18428         this.store.fireEvent("datachanged", this.store);
18429         
18430         this.validate();
18431     },
18432     
18433     clearItem : function()
18434     {
18435         if(!this.multiple){
18436             return;
18437         }
18438         
18439         this.item = [];
18440         
18441         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18442            c.remove();
18443         });
18444         
18445         this.syncValue();
18446         
18447         this.validate();
18448         
18449         if(this.tickable && !Roo.isTouch){
18450             this.view.refresh();
18451         }
18452     },
18453     
18454     inputEl: function ()
18455     {
18456         if(Roo.isIOS && this.useNativeIOS){
18457             return this.el.select('select.roo-ios-select', true).first();
18458         }
18459         
18460         if(Roo.isTouch && this.mobileTouchView){
18461             return this.el.select('input.form-control',true).first();
18462         }
18463         
18464         if(this.tickable){
18465             return this.searchField;
18466         }
18467         
18468         return this.el.select('input.form-control',true).first();
18469     },
18470     
18471     onTickableFooterButtonClick : function(e, btn, el)
18472     {
18473         e.preventDefault();
18474         
18475         this.lastItem = Roo.apply([], this.item);
18476         
18477         if(btn && btn.name == 'cancel'){
18478             this.tickItems = Roo.apply([], this.item);
18479             this.collapse();
18480             return;
18481         }
18482         
18483         this.clearItem();
18484         
18485         var _this = this;
18486         
18487         Roo.each(this.tickItems, function(o){
18488             _this.addItem(o);
18489         });
18490         
18491         this.collapse();
18492         
18493     },
18494     
18495     validate : function()
18496     {
18497         if(this.getVisibilityEl().hasClass('hidden')){
18498             return true;
18499         }
18500         
18501         var v = this.getRawValue();
18502         
18503         if(this.multiple){
18504             v = this.getValue();
18505         }
18506         
18507         if(this.disabled || this.allowBlank || v.length){
18508             this.markValid();
18509             return true;
18510         }
18511         
18512         this.markInvalid();
18513         return false;
18514     },
18515     
18516     tickableInputEl : function()
18517     {
18518         if(!this.tickable || !this.editable){
18519             return this.inputEl();
18520         }
18521         
18522         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18523     },
18524     
18525     
18526     getAutoCreateTouchView : function()
18527     {
18528         var id = Roo.id();
18529         
18530         var cfg = {
18531             cls: 'form-group' //input-group
18532         };
18533         
18534         var input =  {
18535             tag: 'input',
18536             id : id,
18537             type : this.inputType,
18538             cls : 'form-control x-combo-noedit',
18539             autocomplete: 'new-password',
18540             placeholder : this.placeholder || '',
18541             readonly : true
18542         };
18543         
18544         if (this.name) {
18545             input.name = this.name;
18546         }
18547         
18548         if (this.size) {
18549             input.cls += ' input-' + this.size;
18550         }
18551         
18552         if (this.disabled) {
18553             input.disabled = true;
18554         }
18555         
18556         var inputblock = {
18557             cls : 'roo-combobox-wrap',
18558             cn : [
18559                 input
18560             ]
18561         };
18562         
18563         if(this.before){
18564             inputblock.cls += ' input-group';
18565             
18566             inputblock.cn.unshift({
18567                 tag :'span',
18568                 cls : 'input-group-addon input-group-prepend input-group-text',
18569                 html : this.before
18570             });
18571         }
18572         
18573         if(this.removable && !this.multiple){
18574             inputblock.cls += ' roo-removable';
18575             
18576             inputblock.cn.push({
18577                 tag: 'button',
18578                 html : 'x',
18579                 cls : 'roo-combo-removable-btn close'
18580             });
18581         }
18582
18583         if(this.hasFeedback && !this.allowBlank){
18584             
18585             inputblock.cls += ' has-feedback';
18586             
18587             inputblock.cn.push({
18588                 tag: 'span',
18589                 cls: 'glyphicon form-control-feedback'
18590             });
18591             
18592         }
18593         
18594         if (this.after) {
18595             
18596             inputblock.cls += (this.before) ? '' : ' input-group';
18597             
18598             inputblock.cn.push({
18599                 tag :'span',
18600                 cls : 'input-group-addon input-group-append input-group-text',
18601                 html : this.after
18602             });
18603         }
18604
18605         
18606         var ibwrap = inputblock;
18607         
18608         if(this.multiple){
18609             ibwrap = {
18610                 tag: 'ul',
18611                 cls: 'roo-select2-choices',
18612                 cn:[
18613                     {
18614                         tag: 'li',
18615                         cls: 'roo-select2-search-field',
18616                         cn: [
18617
18618                             inputblock
18619                         ]
18620                     }
18621                 ]
18622             };
18623         
18624             
18625         }
18626         
18627         var combobox = {
18628             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18629             cn: [
18630                 {
18631                     tag: 'input',
18632                     type : 'hidden',
18633                     cls: 'form-hidden-field'
18634                 },
18635                 ibwrap
18636             ]
18637         };
18638         
18639         if(!this.multiple && this.showToggleBtn){
18640             
18641             var caret = {
18642                 cls: 'caret'
18643             };
18644             
18645             if (this.caret != false) {
18646                 caret = {
18647                      tag: 'i',
18648                      cls: 'fa fa-' + this.caret
18649                 };
18650                 
18651             }
18652             
18653             combobox.cn.push({
18654                 tag :'span',
18655                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18656                 cn : [
18657                     Roo.bootstrap.version == 3 ? caret : '',
18658                     {
18659                         tag: 'span',
18660                         cls: 'combobox-clear',
18661                         cn  : [
18662                             {
18663                                 tag : 'i',
18664                                 cls: 'icon-remove'
18665                             }
18666                         ]
18667                     }
18668                 ]
18669
18670             })
18671         }
18672         
18673         if(this.multiple){
18674             combobox.cls += ' roo-select2-container-multi';
18675         }
18676         
18677         var required =  this.allowBlank ?  {
18678                     tag : 'i',
18679                     style: 'display: none'
18680                 } : {
18681                    tag : 'i',
18682                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18683                    tooltip : 'This field is required'
18684                 };
18685         
18686         var align = this.labelAlign || this.parentLabelAlign();
18687         
18688         if (align ==='left' && this.fieldLabel.length) {
18689
18690             cfg.cn = [
18691                 required,
18692                 {
18693                     tag: 'label',
18694                     cls : 'control-label col-form-label',
18695                     html : this.fieldLabel
18696
18697                 },
18698                 {
18699                     cls : 'roo-combobox-wrap ', 
18700                     cn: [
18701                         combobox
18702                     ]
18703                 }
18704             ];
18705             
18706             var labelCfg = cfg.cn[1];
18707             var contentCfg = cfg.cn[2];
18708             
18709
18710             if(this.indicatorpos == 'right'){
18711                 cfg.cn = [
18712                     {
18713                         tag: 'label',
18714                         'for' :  id,
18715                         cls : 'control-label col-form-label',
18716                         cn : [
18717                             {
18718                                 tag : 'span',
18719                                 html : this.fieldLabel
18720                             },
18721                             required
18722                         ]
18723                     },
18724                     {
18725                         cls : "roo-combobox-wrap ",
18726                         cn: [
18727                             combobox
18728                         ]
18729                     }
18730
18731                 ];
18732                 
18733                 labelCfg = cfg.cn[0];
18734                 contentCfg = cfg.cn[1];
18735             }
18736             
18737            
18738             
18739             if(this.labelWidth > 12){
18740                 labelCfg.style = "width: " + this.labelWidth + 'px';
18741             }
18742            
18743             if(this.labelWidth < 13 && this.labelmd == 0){
18744                 this.labelmd = this.labelWidth;
18745             }
18746             
18747             if(this.labellg > 0){
18748                 labelCfg.cls += ' col-lg-' + this.labellg;
18749                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18750             }
18751             
18752             if(this.labelmd > 0){
18753                 labelCfg.cls += ' col-md-' + this.labelmd;
18754                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18755             }
18756             
18757             if(this.labelsm > 0){
18758                 labelCfg.cls += ' col-sm-' + this.labelsm;
18759                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18760             }
18761             
18762             if(this.labelxs > 0){
18763                 labelCfg.cls += ' col-xs-' + this.labelxs;
18764                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18765             }
18766                 
18767                 
18768         } else if ( this.fieldLabel.length) {
18769             cfg.cn = [
18770                required,
18771                 {
18772                     tag: 'label',
18773                     cls : 'control-label',
18774                     html : this.fieldLabel
18775
18776                 },
18777                 {
18778                     cls : '', 
18779                     cn: [
18780                         combobox
18781                     ]
18782                 }
18783             ];
18784             
18785             if(this.indicatorpos == 'right'){
18786                 cfg.cn = [
18787                     {
18788                         tag: 'label',
18789                         cls : 'control-label',
18790                         html : this.fieldLabel,
18791                         cn : [
18792                             required
18793                         ]
18794                     },
18795                     {
18796                         cls : '', 
18797                         cn: [
18798                             combobox
18799                         ]
18800                     }
18801                 ];
18802             }
18803         } else {
18804             cfg.cn = combobox;    
18805         }
18806         
18807         
18808         var settings = this;
18809         
18810         ['xs','sm','md','lg'].map(function(size){
18811             if (settings[size]) {
18812                 cfg.cls += ' col-' + size + '-' + settings[size];
18813             }
18814         });
18815         
18816         return cfg;
18817     },
18818     
18819     initTouchView : function()
18820     {
18821         this.renderTouchView();
18822         
18823         this.touchViewEl.on('scroll', function(){
18824             this.el.dom.scrollTop = 0;
18825         }, this);
18826         
18827         this.originalValue = this.getValue();
18828         
18829         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18830         
18831         this.inputEl().on("click", this.showTouchView, this);
18832         if (this.triggerEl) {
18833             this.triggerEl.on("click", this.showTouchView, this);
18834         }
18835         
18836         
18837         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18838         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18839         
18840         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18841         
18842         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18843         this.store.on('load', this.onTouchViewLoad, this);
18844         this.store.on('loadexception', this.onTouchViewLoadException, this);
18845         
18846         if(this.hiddenName){
18847             
18848             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18849             
18850             this.hiddenField.dom.value =
18851                 this.hiddenValue !== undefined ? this.hiddenValue :
18852                 this.value !== undefined ? this.value : '';
18853         
18854             this.el.dom.removeAttribute('name');
18855             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18856         }
18857         
18858         if(this.multiple){
18859             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18860             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18861         }
18862         
18863         if(this.removable && !this.multiple){
18864             var close = this.closeTriggerEl();
18865             if(close){
18866                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18867                 close.on('click', this.removeBtnClick, this, close);
18868             }
18869         }
18870         /*
18871          * fix the bug in Safari iOS8
18872          */
18873         this.inputEl().on("focus", function(e){
18874             document.activeElement.blur();
18875         }, this);
18876         
18877         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18878         
18879         return;
18880         
18881         
18882     },
18883     
18884     renderTouchView : function()
18885     {
18886         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18887         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888         
18889         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18890         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891         
18892         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18893         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18894         this.touchViewBodyEl.setStyle('overflow', 'auto');
18895         
18896         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18897         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18898         
18899         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18900         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18901         
18902     },
18903     
18904     showTouchView : function()
18905     {
18906         if(this.disabled){
18907             return;
18908         }
18909         
18910         this.touchViewHeaderEl.hide();
18911
18912         if(this.modalTitle.length){
18913             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18914             this.touchViewHeaderEl.show();
18915         }
18916
18917         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18918         this.touchViewEl.show();
18919
18920         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18921         
18922         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18923         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18924
18925         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18926
18927         if(this.modalTitle.length){
18928             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18929         }
18930         
18931         this.touchViewBodyEl.setHeight(bodyHeight);
18932
18933         if(this.animate){
18934             var _this = this;
18935             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18936         }else{
18937             this.touchViewEl.addClass(['in','show']);
18938         }
18939         
18940         if(this._touchViewMask){
18941             Roo.get(document.body).addClass("x-body-masked");
18942             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18943             this._touchViewMask.setStyle('z-index', 10000);
18944             this._touchViewMask.addClass('show');
18945         }
18946         
18947         this.doTouchViewQuery();
18948         
18949     },
18950     
18951     hideTouchView : function()
18952     {
18953         this.touchViewEl.removeClass(['in','show']);
18954
18955         if(this.animate){
18956             var _this = this;
18957             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18958         }else{
18959             this.touchViewEl.setStyle('display', 'none');
18960         }
18961         
18962         if(this._touchViewMask){
18963             this._touchViewMask.removeClass('show');
18964             Roo.get(document.body).removeClass("x-body-masked");
18965         }
18966     },
18967     
18968     setTouchViewValue : function()
18969     {
18970         if(this.multiple){
18971             this.clearItem();
18972         
18973             var _this = this;
18974
18975             Roo.each(this.tickItems, function(o){
18976                 this.addItem(o);
18977             }, this);
18978         }
18979         
18980         this.hideTouchView();
18981     },
18982     
18983     doTouchViewQuery : function()
18984     {
18985         var qe = {
18986             query: '',
18987             forceAll: true,
18988             combo: this,
18989             cancel:false
18990         };
18991         
18992         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18993             return false;
18994         }
18995         
18996         if(!this.alwaysQuery || this.mode == 'local'){
18997             this.onTouchViewLoad();
18998             return;
18999         }
19000         
19001         this.store.load();
19002     },
19003     
19004     onTouchViewBeforeLoad : function(combo,opts)
19005     {
19006         return;
19007     },
19008
19009     // private
19010     onTouchViewLoad : function()
19011     {
19012         if(this.store.getCount() < 1){
19013             this.onTouchViewEmptyResults();
19014             return;
19015         }
19016         
19017         this.clearTouchView();
19018         
19019         var rawValue = this.getRawValue();
19020         
19021         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19022         
19023         this.tickItems = [];
19024         
19025         this.store.data.each(function(d, rowIndex){
19026             var row = this.touchViewListGroup.createChild(template);
19027             
19028             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19029                 row.addClass(d.data.cls);
19030             }
19031             
19032             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19033                 var cfg = {
19034                     data : d.data,
19035                     html : d.data[this.displayField]
19036                 };
19037                 
19038                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19039                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19040                 }
19041             }
19042             row.removeClass('selected');
19043             if(!this.multiple && this.valueField &&
19044                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19045             {
19046                 // radio buttons..
19047                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19048                 row.addClass('selected');
19049             }
19050             
19051             if(this.multiple && this.valueField &&
19052                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19053             {
19054                 
19055                 // checkboxes...
19056                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19057                 this.tickItems.push(d.data);
19058             }
19059             
19060             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19061             
19062         }, this);
19063         
19064         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19065         
19066         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19067
19068         if(this.modalTitle.length){
19069             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19070         }
19071
19072         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19073         
19074         if(this.mobile_restrict_height && listHeight < bodyHeight){
19075             this.touchViewBodyEl.setHeight(listHeight);
19076         }
19077         
19078         var _this = this;
19079         
19080         if(firstChecked && listHeight > bodyHeight){
19081             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19082         }
19083         
19084     },
19085     
19086     onTouchViewLoadException : function()
19087     {
19088         this.hideTouchView();
19089     },
19090     
19091     onTouchViewEmptyResults : function()
19092     {
19093         this.clearTouchView();
19094         
19095         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19096         
19097         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19098         
19099     },
19100     
19101     clearTouchView : function()
19102     {
19103         this.touchViewListGroup.dom.innerHTML = '';
19104     },
19105     
19106     onTouchViewClick : function(e, el, o)
19107     {
19108         e.preventDefault();
19109         
19110         var row = o.row;
19111         var rowIndex = o.rowIndex;
19112         
19113         var r = this.store.getAt(rowIndex);
19114         
19115         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19116             
19117             if(!this.multiple){
19118                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19119                     c.dom.removeAttribute('checked');
19120                 }, this);
19121
19122                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19123
19124                 this.setFromData(r.data);
19125
19126                 var close = this.closeTriggerEl();
19127
19128                 if(close){
19129                     close.show();
19130                 }
19131
19132                 this.hideTouchView();
19133
19134                 this.fireEvent('select', this, r, rowIndex);
19135
19136                 return;
19137             }
19138
19139             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19140                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19141                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19142                 return;
19143             }
19144
19145             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19146             this.addItem(r.data);
19147             this.tickItems.push(r.data);
19148         }
19149     },
19150     
19151     getAutoCreateNativeIOS : function()
19152     {
19153         var cfg = {
19154             cls: 'form-group' //input-group,
19155         };
19156         
19157         var combobox =  {
19158             tag: 'select',
19159             cls : 'roo-ios-select'
19160         };
19161         
19162         if (this.name) {
19163             combobox.name = this.name;
19164         }
19165         
19166         if (this.disabled) {
19167             combobox.disabled = true;
19168         }
19169         
19170         var settings = this;
19171         
19172         ['xs','sm','md','lg'].map(function(size){
19173             if (settings[size]) {
19174                 cfg.cls += ' col-' + size + '-' + settings[size];
19175             }
19176         });
19177         
19178         cfg.cn = combobox;
19179         
19180         return cfg;
19181         
19182     },
19183     
19184     initIOSView : function()
19185     {
19186         this.store.on('load', this.onIOSViewLoad, this);
19187         
19188         return;
19189     },
19190     
19191     onIOSViewLoad : function()
19192     {
19193         if(this.store.getCount() < 1){
19194             return;
19195         }
19196         
19197         this.clearIOSView();
19198         
19199         if(this.allowBlank) {
19200             
19201             var default_text = '-- SELECT --';
19202             
19203             if(this.placeholder.length){
19204                 default_text = this.placeholder;
19205             }
19206             
19207             if(this.emptyTitle.length){
19208                 default_text += ' - ' + this.emptyTitle + ' -';
19209             }
19210             
19211             var opt = this.inputEl().createChild({
19212                 tag: 'option',
19213                 value : 0,
19214                 html : default_text
19215             });
19216             
19217             var o = {};
19218             o[this.valueField] = 0;
19219             o[this.displayField] = default_text;
19220             
19221             this.ios_options.push({
19222                 data : o,
19223                 el : opt
19224             });
19225             
19226         }
19227         
19228         this.store.data.each(function(d, rowIndex){
19229             
19230             var html = '';
19231             
19232             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19233                 html = d.data[this.displayField];
19234             }
19235             
19236             var value = '';
19237             
19238             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19239                 value = d.data[this.valueField];
19240             }
19241             
19242             var option = {
19243                 tag: 'option',
19244                 value : value,
19245                 html : html
19246             };
19247             
19248             if(this.value == d.data[this.valueField]){
19249                 option['selected'] = true;
19250             }
19251             
19252             var opt = this.inputEl().createChild(option);
19253             
19254             this.ios_options.push({
19255                 data : d.data,
19256                 el : opt
19257             });
19258             
19259         }, this);
19260         
19261         this.inputEl().on('change', function(){
19262            this.fireEvent('select', this);
19263         }, this);
19264         
19265     },
19266     
19267     clearIOSView: function()
19268     {
19269         this.inputEl().dom.innerHTML = '';
19270         
19271         this.ios_options = [];
19272     },
19273     
19274     setIOSValue: function(v)
19275     {
19276         this.value = v;
19277         
19278         if(!this.ios_options){
19279             return;
19280         }
19281         
19282         Roo.each(this.ios_options, function(opts){
19283            
19284            opts.el.dom.removeAttribute('selected');
19285            
19286            if(opts.data[this.valueField] != v){
19287                return;
19288            }
19289            
19290            opts.el.dom.setAttribute('selected', true);
19291            
19292         }, this);
19293     }
19294
19295     /** 
19296     * @cfg {Boolean} grow 
19297     * @hide 
19298     */
19299     /** 
19300     * @cfg {Number} growMin 
19301     * @hide 
19302     */
19303     /** 
19304     * @cfg {Number} growMax 
19305     * @hide 
19306     */
19307     /**
19308      * @hide
19309      * @method autoSize
19310      */
19311 });
19312
19313 Roo.apply(Roo.bootstrap.ComboBox,  {
19314     
19315     header : {
19316         tag: 'div',
19317         cls: 'modal-header',
19318         cn: [
19319             {
19320                 tag: 'h4',
19321                 cls: 'modal-title'
19322             }
19323         ]
19324     },
19325     
19326     body : {
19327         tag: 'div',
19328         cls: 'modal-body',
19329         cn: [
19330             {
19331                 tag: 'ul',
19332                 cls: 'list-group'
19333             }
19334         ]
19335     },
19336     
19337     listItemRadio : {
19338         tag: 'li',
19339         cls: 'list-group-item',
19340         cn: [
19341             {
19342                 tag: 'span',
19343                 cls: 'roo-combobox-list-group-item-value'
19344             },
19345             {
19346                 tag: 'div',
19347                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19348                 cn: [
19349                     {
19350                         tag: 'input',
19351                         type: 'radio'
19352                     },
19353                     {
19354                         tag: 'label'
19355                     }
19356                 ]
19357             }
19358         ]
19359     },
19360     
19361     listItemCheckbox : {
19362         tag: 'li',
19363         cls: 'list-group-item',
19364         cn: [
19365             {
19366                 tag: 'span',
19367                 cls: 'roo-combobox-list-group-item-value'
19368             },
19369             {
19370                 tag: 'div',
19371                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19372                 cn: [
19373                     {
19374                         tag: 'input',
19375                         type: 'checkbox'
19376                     },
19377                     {
19378                         tag: 'label'
19379                     }
19380                 ]
19381             }
19382         ]
19383     },
19384     
19385     emptyResult : {
19386         tag: 'div',
19387         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19388     },
19389     
19390     footer : {
19391         tag: 'div',
19392         cls: 'modal-footer',
19393         cn: [
19394             {
19395                 tag: 'div',
19396                 cls: 'row',
19397                 cn: [
19398                     {
19399                         tag: 'div',
19400                         cls: 'col-xs-6 text-left',
19401                         cn: {
19402                             tag: 'button',
19403                             cls: 'btn btn-danger roo-touch-view-cancel',
19404                             html: 'Cancel'
19405                         }
19406                     },
19407                     {
19408                         tag: 'div',
19409                         cls: 'col-xs-6 text-right',
19410                         cn: {
19411                             tag: 'button',
19412                             cls: 'btn btn-success roo-touch-view-ok',
19413                             html: 'OK'
19414                         }
19415                     }
19416                 ]
19417             }
19418         ]
19419         
19420     }
19421 });
19422
19423 Roo.apply(Roo.bootstrap.ComboBox,  {
19424     
19425     touchViewTemplate : {
19426         tag: 'div',
19427         cls: 'modal fade roo-combobox-touch-view',
19428         cn: [
19429             {
19430                 tag: 'div',
19431                 cls: 'modal-dialog',
19432                 style : 'position:fixed', // we have to fix position....
19433                 cn: [
19434                     {
19435                         tag: 'div',
19436                         cls: 'modal-content',
19437                         cn: [
19438                             Roo.bootstrap.ComboBox.header,
19439                             Roo.bootstrap.ComboBox.body,
19440                             Roo.bootstrap.ComboBox.footer
19441                         ]
19442                     }
19443                 ]
19444             }
19445         ]
19446     }
19447 });/*
19448  * Based on:
19449  * Ext JS Library 1.1.1
19450  * Copyright(c) 2006-2007, Ext JS, LLC.
19451  *
19452  * Originally Released Under LGPL - original licence link has changed is not relivant.
19453  *
19454  * Fork - LGPL
19455  * <script type="text/javascript">
19456  */
19457
19458 /**
19459  * @class Roo.View
19460  * @extends Roo.util.Observable
19461  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19462  * This class also supports single and multi selection modes. <br>
19463  * Create a data model bound view:
19464  <pre><code>
19465  var store = new Roo.data.Store(...);
19466
19467  var view = new Roo.View({
19468     el : "my-element",
19469     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19470  
19471     singleSelect: true,
19472     selectedClass: "ydataview-selected",
19473     store: store
19474  });
19475
19476  // listen for node click?
19477  view.on("click", function(vw, index, node, e){
19478  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19479  });
19480
19481  // load XML data
19482  dataModel.load("foobar.xml");
19483  </code></pre>
19484  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19485  * <br><br>
19486  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19487  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19488  * 
19489  * Note: old style constructor is still suported (container, template, config)
19490  * 
19491  * @constructor
19492  * Create a new View
19493  * @param {Object} config The config object
19494  * 
19495  */
19496 Roo.View = function(config, depreciated_tpl, depreciated_config){
19497     
19498     this.parent = false;
19499     
19500     if (typeof(depreciated_tpl) == 'undefined') {
19501         // new way.. - universal constructor.
19502         Roo.apply(this, config);
19503         this.el  = Roo.get(this.el);
19504     } else {
19505         // old format..
19506         this.el  = Roo.get(config);
19507         this.tpl = depreciated_tpl;
19508         Roo.apply(this, depreciated_config);
19509     }
19510     this.wrapEl  = this.el.wrap().wrap();
19511     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19512     
19513     
19514     if(typeof(this.tpl) == "string"){
19515         this.tpl = new Roo.Template(this.tpl);
19516     } else {
19517         // support xtype ctors..
19518         this.tpl = new Roo.factory(this.tpl, Roo);
19519     }
19520     
19521     
19522     this.tpl.compile();
19523     
19524     /** @private */
19525     this.addEvents({
19526         /**
19527          * @event beforeclick
19528          * Fires before a click is processed. Returns false to cancel the default action.
19529          * @param {Roo.View} this
19530          * @param {Number} index The index of the target node
19531          * @param {HTMLElement} node The target node
19532          * @param {Roo.EventObject} e The raw event object
19533          */
19534             "beforeclick" : true,
19535         /**
19536          * @event click
19537          * Fires when a template node is clicked.
19538          * @param {Roo.View} this
19539          * @param {Number} index The index of the target node
19540          * @param {HTMLElement} node The target node
19541          * @param {Roo.EventObject} e The raw event object
19542          */
19543             "click" : true,
19544         /**
19545          * @event dblclick
19546          * Fires when a template node is double clicked.
19547          * @param {Roo.View} this
19548          * @param {Number} index The index of the target node
19549          * @param {HTMLElement} node The target node
19550          * @param {Roo.EventObject} e The raw event object
19551          */
19552             "dblclick" : true,
19553         /**
19554          * @event contextmenu
19555          * Fires when a template node is right clicked.
19556          * @param {Roo.View} this
19557          * @param {Number} index The index of the target node
19558          * @param {HTMLElement} node The target node
19559          * @param {Roo.EventObject} e The raw event object
19560          */
19561             "contextmenu" : true,
19562         /**
19563          * @event selectionchange
19564          * Fires when the selected nodes change.
19565          * @param {Roo.View} this
19566          * @param {Array} selections Array of the selected nodes
19567          */
19568             "selectionchange" : true,
19569     
19570         /**
19571          * @event beforeselect
19572          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19573          * @param {Roo.View} this
19574          * @param {HTMLElement} node The node to be selected
19575          * @param {Array} selections Array of currently selected nodes
19576          */
19577             "beforeselect" : true,
19578         /**
19579          * @event preparedata
19580          * Fires on every row to render, to allow you to change the data.
19581          * @param {Roo.View} this
19582          * @param {Object} data to be rendered (change this)
19583          */
19584           "preparedata" : true
19585           
19586           
19587         });
19588
19589
19590
19591     this.el.on({
19592         "click": this.onClick,
19593         "dblclick": this.onDblClick,
19594         "contextmenu": this.onContextMenu,
19595         scope:this
19596     });
19597
19598     this.selections = [];
19599     this.nodes = [];
19600     this.cmp = new Roo.CompositeElementLite([]);
19601     if(this.store){
19602         this.store = Roo.factory(this.store, Roo.data);
19603         this.setStore(this.store, true);
19604     }
19605     
19606     if ( this.footer && this.footer.xtype) {
19607            
19608          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19609         
19610         this.footer.dataSource = this.store;
19611         this.footer.container = fctr;
19612         this.footer = Roo.factory(this.footer, Roo);
19613         fctr.insertFirst(this.el);
19614         
19615         // this is a bit insane - as the paging toolbar seems to detach the el..
19616 //        dom.parentNode.parentNode.parentNode
19617          // they get detached?
19618     }
19619     
19620     
19621     Roo.View.superclass.constructor.call(this);
19622     
19623     
19624 };
19625
19626 Roo.extend(Roo.View, Roo.util.Observable, {
19627     
19628      /**
19629      * @cfg {Roo.data.Store} store Data store to load data from.
19630      */
19631     store : false,
19632     
19633     /**
19634      * @cfg {String|Roo.Element} el The container element.
19635      */
19636     el : '',
19637     
19638     /**
19639      * @cfg {String|Roo.Template} tpl The template used by this View 
19640      */
19641     tpl : false,
19642     /**
19643      * @cfg {String} dataName the named area of the template to use as the data area
19644      *                          Works with domtemplates roo-name="name"
19645      */
19646     dataName: false,
19647     /**
19648      * @cfg {String} selectedClass The css class to add to selected nodes
19649      */
19650     selectedClass : "x-view-selected",
19651      /**
19652      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19653      */
19654     emptyText : "",
19655     
19656     /**
19657      * @cfg {String} text to display on mask (default Loading)
19658      */
19659     mask : false,
19660     /**
19661      * @cfg {Boolean} multiSelect Allow multiple selection
19662      */
19663     multiSelect : false,
19664     /**
19665      * @cfg {Boolean} singleSelect Allow single selection
19666      */
19667     singleSelect:  false,
19668     
19669     /**
19670      * @cfg {Boolean} toggleSelect - selecting 
19671      */
19672     toggleSelect : false,
19673     
19674     /**
19675      * @cfg {Boolean} tickable - selecting 
19676      */
19677     tickable : false,
19678     
19679     /**
19680      * Returns the element this view is bound to.
19681      * @return {Roo.Element}
19682      */
19683     getEl : function(){
19684         return this.wrapEl;
19685     },
19686     
19687     
19688
19689     /**
19690      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19691      */
19692     refresh : function(){
19693         //Roo.log('refresh');
19694         var t = this.tpl;
19695         
19696         // if we are using something like 'domtemplate', then
19697         // the what gets used is:
19698         // t.applySubtemplate(NAME, data, wrapping data..)
19699         // the outer template then get' applied with
19700         //     the store 'extra data'
19701         // and the body get's added to the
19702         //      roo-name="data" node?
19703         //      <span class='roo-tpl-{name}'></span> ?????
19704         
19705         
19706         
19707         this.clearSelections();
19708         this.el.update("");
19709         var html = [];
19710         var records = this.store.getRange();
19711         if(records.length < 1) {
19712             
19713             // is this valid??  = should it render a template??
19714             
19715             this.el.update(this.emptyText);
19716             return;
19717         }
19718         var el = this.el;
19719         if (this.dataName) {
19720             this.el.update(t.apply(this.store.meta)); //????
19721             el = this.el.child('.roo-tpl-' + this.dataName);
19722         }
19723         
19724         for(var i = 0, len = records.length; i < len; i++){
19725             var data = this.prepareData(records[i].data, i, records[i]);
19726             this.fireEvent("preparedata", this, data, i, records[i]);
19727             
19728             var d = Roo.apply({}, data);
19729             
19730             if(this.tickable){
19731                 Roo.apply(d, {'roo-id' : Roo.id()});
19732                 
19733                 var _this = this;
19734             
19735                 Roo.each(this.parent.item, function(item){
19736                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19737                         return;
19738                     }
19739                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19740                 });
19741             }
19742             
19743             html[html.length] = Roo.util.Format.trim(
19744                 this.dataName ?
19745                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19746                     t.apply(d)
19747             );
19748         }
19749         
19750         
19751         
19752         el.update(html.join(""));
19753         this.nodes = el.dom.childNodes;
19754         this.updateIndexes(0);
19755     },
19756     
19757
19758     /**
19759      * Function to override to reformat the data that is sent to
19760      * the template for each node.
19761      * DEPRICATED - use the preparedata event handler.
19762      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19763      * a JSON object for an UpdateManager bound view).
19764      */
19765     prepareData : function(data, index, record)
19766     {
19767         this.fireEvent("preparedata", this, data, index, record);
19768         return data;
19769     },
19770
19771     onUpdate : function(ds, record){
19772         // Roo.log('on update');   
19773         this.clearSelections();
19774         var index = this.store.indexOf(record);
19775         var n = this.nodes[index];
19776         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19777         n.parentNode.removeChild(n);
19778         this.updateIndexes(index, index);
19779     },
19780
19781     
19782     
19783 // --------- FIXME     
19784     onAdd : function(ds, records, index)
19785     {
19786         //Roo.log(['on Add', ds, records, index] );        
19787         this.clearSelections();
19788         if(this.nodes.length == 0){
19789             this.refresh();
19790             return;
19791         }
19792         var n = this.nodes[index];
19793         for(var i = 0, len = records.length; i < len; i++){
19794             var d = this.prepareData(records[i].data, i, records[i]);
19795             if(n){
19796                 this.tpl.insertBefore(n, d);
19797             }else{
19798                 
19799                 this.tpl.append(this.el, d);
19800             }
19801         }
19802         this.updateIndexes(index);
19803     },
19804
19805     onRemove : function(ds, record, index){
19806        // Roo.log('onRemove');
19807         this.clearSelections();
19808         var el = this.dataName  ?
19809             this.el.child('.roo-tpl-' + this.dataName) :
19810             this.el; 
19811         
19812         el.dom.removeChild(this.nodes[index]);
19813         this.updateIndexes(index);
19814     },
19815
19816     /**
19817      * Refresh an individual node.
19818      * @param {Number} index
19819      */
19820     refreshNode : function(index){
19821         this.onUpdate(this.store, this.store.getAt(index));
19822     },
19823
19824     updateIndexes : function(startIndex, endIndex){
19825         var ns = this.nodes;
19826         startIndex = startIndex || 0;
19827         endIndex = endIndex || ns.length - 1;
19828         for(var i = startIndex; i <= endIndex; i++){
19829             ns[i].nodeIndex = i;
19830         }
19831     },
19832
19833     /**
19834      * Changes the data store this view uses and refresh the view.
19835      * @param {Store} store
19836      */
19837     setStore : function(store, initial){
19838         if(!initial && this.store){
19839             this.store.un("datachanged", this.refresh);
19840             this.store.un("add", this.onAdd);
19841             this.store.un("remove", this.onRemove);
19842             this.store.un("update", this.onUpdate);
19843             this.store.un("clear", this.refresh);
19844             this.store.un("beforeload", this.onBeforeLoad);
19845             this.store.un("load", this.onLoad);
19846             this.store.un("loadexception", this.onLoad);
19847         }
19848         if(store){
19849           
19850             store.on("datachanged", this.refresh, this);
19851             store.on("add", this.onAdd, this);
19852             store.on("remove", this.onRemove, this);
19853             store.on("update", this.onUpdate, this);
19854             store.on("clear", this.refresh, this);
19855             store.on("beforeload", this.onBeforeLoad, this);
19856             store.on("load", this.onLoad, this);
19857             store.on("loadexception", this.onLoad, this);
19858         }
19859         
19860         if(store){
19861             this.refresh();
19862         }
19863     },
19864     /**
19865      * onbeforeLoad - masks the loading area.
19866      *
19867      */
19868     onBeforeLoad : function(store,opts)
19869     {
19870          //Roo.log('onBeforeLoad');   
19871         if (!opts.add) {
19872             this.el.update("");
19873         }
19874         this.el.mask(this.mask ? this.mask : "Loading" ); 
19875     },
19876     onLoad : function ()
19877     {
19878         this.el.unmask();
19879     },
19880     
19881
19882     /**
19883      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19884      * @param {HTMLElement} node
19885      * @return {HTMLElement} The template node
19886      */
19887     findItemFromChild : function(node){
19888         var el = this.dataName  ?
19889             this.el.child('.roo-tpl-' + this.dataName,true) :
19890             this.el.dom; 
19891         
19892         if(!node || node.parentNode == el){
19893                     return node;
19894             }
19895             var p = node.parentNode;
19896             while(p && p != el){
19897             if(p.parentNode == el){
19898                 return p;
19899             }
19900             p = p.parentNode;
19901         }
19902             return null;
19903     },
19904
19905     /** @ignore */
19906     onClick : function(e){
19907         var item = this.findItemFromChild(e.getTarget());
19908         if(item){
19909             var index = this.indexOf(item);
19910             if(this.onItemClick(item, index, e) !== false){
19911                 this.fireEvent("click", this, index, item, e);
19912             }
19913         }else{
19914             this.clearSelections();
19915         }
19916     },
19917
19918     /** @ignore */
19919     onContextMenu : function(e){
19920         var item = this.findItemFromChild(e.getTarget());
19921         if(item){
19922             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19923         }
19924     },
19925
19926     /** @ignore */
19927     onDblClick : function(e){
19928         var item = this.findItemFromChild(e.getTarget());
19929         if(item){
19930             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19931         }
19932     },
19933
19934     onItemClick : function(item, index, e)
19935     {
19936         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19937             return false;
19938         }
19939         if (this.toggleSelect) {
19940             var m = this.isSelected(item) ? 'unselect' : 'select';
19941             //Roo.log(m);
19942             var _t = this;
19943             _t[m](item, true, false);
19944             return true;
19945         }
19946         if(this.multiSelect || this.singleSelect){
19947             if(this.multiSelect && e.shiftKey && this.lastSelection){
19948                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19949             }else{
19950                 this.select(item, this.multiSelect && e.ctrlKey);
19951                 this.lastSelection = item;
19952             }
19953             
19954             if(!this.tickable){
19955                 e.preventDefault();
19956             }
19957             
19958         }
19959         return true;
19960     },
19961
19962     /**
19963      * Get the number of selected nodes.
19964      * @return {Number}
19965      */
19966     getSelectionCount : function(){
19967         return this.selections.length;
19968     },
19969
19970     /**
19971      * Get the currently selected nodes.
19972      * @return {Array} An array of HTMLElements
19973      */
19974     getSelectedNodes : function(){
19975         return this.selections;
19976     },
19977
19978     /**
19979      * Get the indexes of the selected nodes.
19980      * @return {Array}
19981      */
19982     getSelectedIndexes : function(){
19983         var indexes = [], s = this.selections;
19984         for(var i = 0, len = s.length; i < len; i++){
19985             indexes.push(s[i].nodeIndex);
19986         }
19987         return indexes;
19988     },
19989
19990     /**
19991      * Clear all selections
19992      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19993      */
19994     clearSelections : function(suppressEvent){
19995         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19996             this.cmp.elements = this.selections;
19997             this.cmp.removeClass(this.selectedClass);
19998             this.selections = [];
19999             if(!suppressEvent){
20000                 this.fireEvent("selectionchange", this, this.selections);
20001             }
20002         }
20003     },
20004
20005     /**
20006      * Returns true if the passed node is selected
20007      * @param {HTMLElement/Number} node The node or node index
20008      * @return {Boolean}
20009      */
20010     isSelected : function(node){
20011         var s = this.selections;
20012         if(s.length < 1){
20013             return false;
20014         }
20015         node = this.getNode(node);
20016         return s.indexOf(node) !== -1;
20017     },
20018
20019     /**
20020      * Selects nodes.
20021      * @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
20022      * @param {Boolean} keepExisting (optional) true to keep existing selections
20023      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20024      */
20025     select : function(nodeInfo, keepExisting, suppressEvent){
20026         if(nodeInfo instanceof Array){
20027             if(!keepExisting){
20028                 this.clearSelections(true);
20029             }
20030             for(var i = 0, len = nodeInfo.length; i < len; i++){
20031                 this.select(nodeInfo[i], true, true);
20032             }
20033             return;
20034         } 
20035         var node = this.getNode(nodeInfo);
20036         if(!node || this.isSelected(node)){
20037             return; // already selected.
20038         }
20039         if(!keepExisting){
20040             this.clearSelections(true);
20041         }
20042         
20043         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20044             Roo.fly(node).addClass(this.selectedClass);
20045             this.selections.push(node);
20046             if(!suppressEvent){
20047                 this.fireEvent("selectionchange", this, this.selections);
20048             }
20049         }
20050         
20051         
20052     },
20053       /**
20054      * Unselects nodes.
20055      * @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
20056      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20057      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20058      */
20059     unselect : function(nodeInfo, keepExisting, suppressEvent)
20060     {
20061         if(nodeInfo instanceof Array){
20062             Roo.each(this.selections, function(s) {
20063                 this.unselect(s, nodeInfo);
20064             }, this);
20065             return;
20066         }
20067         var node = this.getNode(nodeInfo);
20068         if(!node || !this.isSelected(node)){
20069             //Roo.log("not selected");
20070             return; // not selected.
20071         }
20072         // fireevent???
20073         var ns = [];
20074         Roo.each(this.selections, function(s) {
20075             if (s == node ) {
20076                 Roo.fly(node).removeClass(this.selectedClass);
20077
20078                 return;
20079             }
20080             ns.push(s);
20081         },this);
20082         
20083         this.selections= ns;
20084         this.fireEvent("selectionchange", this, this.selections);
20085     },
20086
20087     /**
20088      * Gets a template node.
20089      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20090      * @return {HTMLElement} The node or null if it wasn't found
20091      */
20092     getNode : function(nodeInfo){
20093         if(typeof nodeInfo == "string"){
20094             return document.getElementById(nodeInfo);
20095         }else if(typeof nodeInfo == "number"){
20096             return this.nodes[nodeInfo];
20097         }
20098         return nodeInfo;
20099     },
20100
20101     /**
20102      * Gets a range template nodes.
20103      * @param {Number} startIndex
20104      * @param {Number} endIndex
20105      * @return {Array} An array of nodes
20106      */
20107     getNodes : function(start, end){
20108         var ns = this.nodes;
20109         start = start || 0;
20110         end = typeof end == "undefined" ? ns.length - 1 : end;
20111         var nodes = [];
20112         if(start <= end){
20113             for(var i = start; i <= end; i++){
20114                 nodes.push(ns[i]);
20115             }
20116         } else{
20117             for(var i = start; i >= end; i--){
20118                 nodes.push(ns[i]);
20119             }
20120         }
20121         return nodes;
20122     },
20123
20124     /**
20125      * Finds the index of the passed node
20126      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20127      * @return {Number} The index of the node or -1
20128      */
20129     indexOf : function(node){
20130         node = this.getNode(node);
20131         if(typeof node.nodeIndex == "number"){
20132             return node.nodeIndex;
20133         }
20134         var ns = this.nodes;
20135         for(var i = 0, len = ns.length; i < len; i++){
20136             if(ns[i] == node){
20137                 return i;
20138             }
20139         }
20140         return -1;
20141     }
20142 });
20143 /*
20144  * - LGPL
20145  *
20146  * based on jquery fullcalendar
20147  * 
20148  */
20149
20150 Roo.bootstrap = Roo.bootstrap || {};
20151 /**
20152  * @class Roo.bootstrap.Calendar
20153  * @extends Roo.bootstrap.Component
20154  * Bootstrap Calendar class
20155  * @cfg {Boolean} loadMask (true|false) default false
20156  * @cfg {Object} header generate the user specific header of the calendar, default false
20157
20158  * @constructor
20159  * Create a new Container
20160  * @param {Object} config The config object
20161  */
20162
20163
20164
20165 Roo.bootstrap.Calendar = function(config){
20166     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20167      this.addEvents({
20168         /**
20169              * @event select
20170              * Fires when a date is selected
20171              * @param {DatePicker} this
20172              * @param {Date} date The selected date
20173              */
20174         'select': true,
20175         /**
20176              * @event monthchange
20177              * Fires when the displayed month changes 
20178              * @param {DatePicker} this
20179              * @param {Date} date The selected month
20180              */
20181         'monthchange': true,
20182         /**
20183              * @event evententer
20184              * Fires when mouse over an event
20185              * @param {Calendar} this
20186              * @param {event} Event
20187              */
20188         'evententer': true,
20189         /**
20190              * @event eventleave
20191              * Fires when the mouse leaves an
20192              * @param {Calendar} this
20193              * @param {event}
20194              */
20195         'eventleave': true,
20196         /**
20197              * @event eventclick
20198              * Fires when the mouse click an
20199              * @param {Calendar} this
20200              * @param {event}
20201              */
20202         'eventclick': true
20203         
20204     });
20205
20206 };
20207
20208 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20209     
20210           /**
20211      * @cfg {Roo.data.Store} store
20212      * The data source for the calendar
20213      */
20214         store : false,
20215      /**
20216      * @cfg {Number} startDay
20217      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20218      */
20219     startDay : 0,
20220     
20221     loadMask : false,
20222     
20223     header : false,
20224       
20225     getAutoCreate : function(){
20226         
20227         
20228         var fc_button = function(name, corner, style, content ) {
20229             return Roo.apply({},{
20230                 tag : 'span',
20231                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20232                          (corner.length ?
20233                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20234                             ''
20235                         ),
20236                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20237                 unselectable: 'on'
20238             });
20239         };
20240         
20241         var header = {};
20242         
20243         if(!this.header){
20244             header = {
20245                 tag : 'table',
20246                 cls : 'fc-header',
20247                 style : 'width:100%',
20248                 cn : [
20249                     {
20250                         tag: 'tr',
20251                         cn : [
20252                             {
20253                                 tag : 'td',
20254                                 cls : 'fc-header-left',
20255                                 cn : [
20256                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20257                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20258                                     { tag: 'span', cls: 'fc-header-space' },
20259                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20260
20261
20262                                 ]
20263                             },
20264
20265                             {
20266                                 tag : 'td',
20267                                 cls : 'fc-header-center',
20268                                 cn : [
20269                                     {
20270                                         tag: 'span',
20271                                         cls: 'fc-header-title',
20272                                         cn : {
20273                                             tag: 'H2',
20274                                             html : 'month / year'
20275                                         }
20276                                     }
20277
20278                                 ]
20279                             },
20280                             {
20281                                 tag : 'td',
20282                                 cls : 'fc-header-right',
20283                                 cn : [
20284                               /*      fc_button('month', 'left', '', 'month' ),
20285                                     fc_button('week', '', '', 'week' ),
20286                                     fc_button('day', 'right', '', 'day' )
20287                                 */    
20288
20289                                 ]
20290                             }
20291
20292                         ]
20293                     }
20294                 ]
20295             };
20296         }
20297         
20298         header = this.header;
20299         
20300        
20301         var cal_heads = function() {
20302             var ret = [];
20303             // fixme - handle this.
20304             
20305             for (var i =0; i < Date.dayNames.length; i++) {
20306                 var d = Date.dayNames[i];
20307                 ret.push({
20308                     tag: 'th',
20309                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20310                     html : d.substring(0,3)
20311                 });
20312                 
20313             }
20314             ret[0].cls += ' fc-first';
20315             ret[6].cls += ' fc-last';
20316             return ret;
20317         };
20318         var cal_cell = function(n) {
20319             return  {
20320                 tag: 'td',
20321                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20322                 cn : [
20323                     {
20324                         cn : [
20325                             {
20326                                 cls: 'fc-day-number',
20327                                 html: 'D'
20328                             },
20329                             {
20330                                 cls: 'fc-day-content',
20331                              
20332                                 cn : [
20333                                      {
20334                                         style: 'position: relative;' // height: 17px;
20335                                     }
20336                                 ]
20337                             }
20338                             
20339                             
20340                         ]
20341                     }
20342                 ]
20343                 
20344             }
20345         };
20346         var cal_rows = function() {
20347             
20348             var ret = [];
20349             for (var r = 0; r < 6; r++) {
20350                 var row= {
20351                     tag : 'tr',
20352                     cls : 'fc-week',
20353                     cn : []
20354                 };
20355                 
20356                 for (var i =0; i < Date.dayNames.length; i++) {
20357                     var d = Date.dayNames[i];
20358                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20359
20360                 }
20361                 row.cn[0].cls+=' fc-first';
20362                 row.cn[0].cn[0].style = 'min-height:90px';
20363                 row.cn[6].cls+=' fc-last';
20364                 ret.push(row);
20365                 
20366             }
20367             ret[0].cls += ' fc-first';
20368             ret[4].cls += ' fc-prev-last';
20369             ret[5].cls += ' fc-last';
20370             return ret;
20371             
20372         };
20373         
20374         var cal_table = {
20375             tag: 'table',
20376             cls: 'fc-border-separate',
20377             style : 'width:100%',
20378             cellspacing  : 0,
20379             cn : [
20380                 { 
20381                     tag: 'thead',
20382                     cn : [
20383                         { 
20384                             tag: 'tr',
20385                             cls : 'fc-first fc-last',
20386                             cn : cal_heads()
20387                         }
20388                     ]
20389                 },
20390                 { 
20391                     tag: 'tbody',
20392                     cn : cal_rows()
20393                 }
20394                   
20395             ]
20396         };
20397          
20398          var cfg = {
20399             cls : 'fc fc-ltr',
20400             cn : [
20401                 header,
20402                 {
20403                     cls : 'fc-content',
20404                     style : "position: relative;",
20405                     cn : [
20406                         {
20407                             cls : 'fc-view fc-view-month fc-grid',
20408                             style : 'position: relative',
20409                             unselectable : 'on',
20410                             cn : [
20411                                 {
20412                                     cls : 'fc-event-container',
20413                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20414                                 },
20415                                 cal_table
20416                             ]
20417                         }
20418                     ]
20419     
20420                 }
20421            ] 
20422             
20423         };
20424         
20425          
20426         
20427         return cfg;
20428     },
20429     
20430     
20431     initEvents : function()
20432     {
20433         if(!this.store){
20434             throw "can not find store for calendar";
20435         }
20436         
20437         var mark = {
20438             tag: "div",
20439             cls:"x-dlg-mask",
20440             style: "text-align:center",
20441             cn: [
20442                 {
20443                     tag: "div",
20444                     style: "background-color:white;width:50%;margin:250 auto",
20445                     cn: [
20446                         {
20447                             tag: "img",
20448                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20449                         },
20450                         {
20451                             tag: "span",
20452                             html: "Loading"
20453                         }
20454                         
20455                     ]
20456                 }
20457             ]
20458         };
20459         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20460         
20461         var size = this.el.select('.fc-content', true).first().getSize();
20462         this.maskEl.setSize(size.width, size.height);
20463         this.maskEl.enableDisplayMode("block");
20464         if(!this.loadMask){
20465             this.maskEl.hide();
20466         }
20467         
20468         this.store = Roo.factory(this.store, Roo.data);
20469         this.store.on('load', this.onLoad, this);
20470         this.store.on('beforeload', this.onBeforeLoad, this);
20471         
20472         this.resize();
20473         
20474         this.cells = this.el.select('.fc-day',true);
20475         //Roo.log(this.cells);
20476         this.textNodes = this.el.query('.fc-day-number');
20477         this.cells.addClassOnOver('fc-state-hover');
20478         
20479         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20480         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20481         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20482         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20483         
20484         this.on('monthchange', this.onMonthChange, this);
20485         
20486         this.update(new Date().clearTime());
20487     },
20488     
20489     resize : function() {
20490         var sz  = this.el.getSize();
20491         
20492         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20493         this.el.select('.fc-day-content div',true).setHeight(34);
20494     },
20495     
20496     
20497     // private
20498     showPrevMonth : function(e){
20499         this.update(this.activeDate.add("mo", -1));
20500     },
20501     showToday : function(e){
20502         this.update(new Date().clearTime());
20503     },
20504     // private
20505     showNextMonth : function(e){
20506         this.update(this.activeDate.add("mo", 1));
20507     },
20508
20509     // private
20510     showPrevYear : function(){
20511         this.update(this.activeDate.add("y", -1));
20512     },
20513
20514     // private
20515     showNextYear : function(){
20516         this.update(this.activeDate.add("y", 1));
20517     },
20518
20519     
20520    // private
20521     update : function(date)
20522     {
20523         var vd = this.activeDate;
20524         this.activeDate = date;
20525 //        if(vd && this.el){
20526 //            var t = date.getTime();
20527 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20528 //                Roo.log('using add remove');
20529 //                
20530 //                this.fireEvent('monthchange', this, date);
20531 //                
20532 //                this.cells.removeClass("fc-state-highlight");
20533 //                this.cells.each(function(c){
20534 //                   if(c.dateValue == t){
20535 //                       c.addClass("fc-state-highlight");
20536 //                       setTimeout(function(){
20537 //                            try{c.dom.firstChild.focus();}catch(e){}
20538 //                       }, 50);
20539 //                       return false;
20540 //                   }
20541 //                   return true;
20542 //                });
20543 //                return;
20544 //            }
20545 //        }
20546         
20547         var days = date.getDaysInMonth();
20548         
20549         var firstOfMonth = date.getFirstDateOfMonth();
20550         var startingPos = firstOfMonth.getDay()-this.startDay;
20551         
20552         if(startingPos < this.startDay){
20553             startingPos += 7;
20554         }
20555         
20556         var pm = date.add(Date.MONTH, -1);
20557         var prevStart = pm.getDaysInMonth()-startingPos;
20558 //        
20559         this.cells = this.el.select('.fc-day',true);
20560         this.textNodes = this.el.query('.fc-day-number');
20561         this.cells.addClassOnOver('fc-state-hover');
20562         
20563         var cells = this.cells.elements;
20564         var textEls = this.textNodes;
20565         
20566         Roo.each(cells, function(cell){
20567             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20568         });
20569         
20570         days += startingPos;
20571
20572         // convert everything to numbers so it's fast
20573         var day = 86400000;
20574         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20575         //Roo.log(d);
20576         //Roo.log(pm);
20577         //Roo.log(prevStart);
20578         
20579         var today = new Date().clearTime().getTime();
20580         var sel = date.clearTime().getTime();
20581         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20582         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20583         var ddMatch = this.disabledDatesRE;
20584         var ddText = this.disabledDatesText;
20585         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20586         var ddaysText = this.disabledDaysText;
20587         var format = this.format;
20588         
20589         var setCellClass = function(cal, cell){
20590             cell.row = 0;
20591             cell.events = [];
20592             cell.more = [];
20593             //Roo.log('set Cell Class');
20594             cell.title = "";
20595             var t = d.getTime();
20596             
20597             //Roo.log(d);
20598             
20599             cell.dateValue = t;
20600             if(t == today){
20601                 cell.className += " fc-today";
20602                 cell.className += " fc-state-highlight";
20603                 cell.title = cal.todayText;
20604             }
20605             if(t == sel){
20606                 // disable highlight in other month..
20607                 //cell.className += " fc-state-highlight";
20608                 
20609             }
20610             // disabling
20611             if(t < min) {
20612                 cell.className = " fc-state-disabled";
20613                 cell.title = cal.minText;
20614                 return;
20615             }
20616             if(t > max) {
20617                 cell.className = " fc-state-disabled";
20618                 cell.title = cal.maxText;
20619                 return;
20620             }
20621             if(ddays){
20622                 if(ddays.indexOf(d.getDay()) != -1){
20623                     cell.title = ddaysText;
20624                     cell.className = " fc-state-disabled";
20625                 }
20626             }
20627             if(ddMatch && format){
20628                 var fvalue = d.dateFormat(format);
20629                 if(ddMatch.test(fvalue)){
20630                     cell.title = ddText.replace("%0", fvalue);
20631                     cell.className = " fc-state-disabled";
20632                 }
20633             }
20634             
20635             if (!cell.initialClassName) {
20636                 cell.initialClassName = cell.dom.className;
20637             }
20638             
20639             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20640         };
20641
20642         var i = 0;
20643         
20644         for(; i < startingPos; i++) {
20645             textEls[i].innerHTML = (++prevStart);
20646             d.setDate(d.getDate()+1);
20647             
20648             cells[i].className = "fc-past fc-other-month";
20649             setCellClass(this, cells[i]);
20650         }
20651         
20652         var intDay = 0;
20653         
20654         for(; i < days; i++){
20655             intDay = i - startingPos + 1;
20656             textEls[i].innerHTML = (intDay);
20657             d.setDate(d.getDate()+1);
20658             
20659             cells[i].className = ''; // "x-date-active";
20660             setCellClass(this, cells[i]);
20661         }
20662         var extraDays = 0;
20663         
20664         for(; i < 42; i++) {
20665             textEls[i].innerHTML = (++extraDays);
20666             d.setDate(d.getDate()+1);
20667             
20668             cells[i].className = "fc-future fc-other-month";
20669             setCellClass(this, cells[i]);
20670         }
20671         
20672         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20673         
20674         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20675         
20676         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20677         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20678         
20679         if(totalRows != 6){
20680             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20681             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20682         }
20683         
20684         this.fireEvent('monthchange', this, date);
20685         
20686         
20687         /*
20688         if(!this.internalRender){
20689             var main = this.el.dom.firstChild;
20690             var w = main.offsetWidth;
20691             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20692             Roo.fly(main).setWidth(w);
20693             this.internalRender = true;
20694             // opera does not respect the auto grow header center column
20695             // then, after it gets a width opera refuses to recalculate
20696             // without a second pass
20697             if(Roo.isOpera && !this.secondPass){
20698                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20699                 this.secondPass = true;
20700                 this.update.defer(10, this, [date]);
20701             }
20702         }
20703         */
20704         
20705     },
20706     
20707     findCell : function(dt) {
20708         dt = dt.clearTime().getTime();
20709         var ret = false;
20710         this.cells.each(function(c){
20711             //Roo.log("check " +c.dateValue + '?=' + dt);
20712             if(c.dateValue == dt){
20713                 ret = c;
20714                 return false;
20715             }
20716             return true;
20717         });
20718         
20719         return ret;
20720     },
20721     
20722     findCells : function(ev) {
20723         var s = ev.start.clone().clearTime().getTime();
20724        // Roo.log(s);
20725         var e= ev.end.clone().clearTime().getTime();
20726        // Roo.log(e);
20727         var ret = [];
20728         this.cells.each(function(c){
20729              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20730             
20731             if(c.dateValue > e){
20732                 return ;
20733             }
20734             if(c.dateValue < s){
20735                 return ;
20736             }
20737             ret.push(c);
20738         });
20739         
20740         return ret;    
20741     },
20742     
20743 //    findBestRow: function(cells)
20744 //    {
20745 //        var ret = 0;
20746 //        
20747 //        for (var i =0 ; i < cells.length;i++) {
20748 //            ret  = Math.max(cells[i].rows || 0,ret);
20749 //        }
20750 //        return ret;
20751 //        
20752 //    },
20753     
20754     
20755     addItem : function(ev)
20756     {
20757         // look for vertical location slot in
20758         var cells = this.findCells(ev);
20759         
20760 //        ev.row = this.findBestRow(cells);
20761         
20762         // work out the location.
20763         
20764         var crow = false;
20765         var rows = [];
20766         for(var i =0; i < cells.length; i++) {
20767             
20768             cells[i].row = cells[0].row;
20769             
20770             if(i == 0){
20771                 cells[i].row = cells[i].row + 1;
20772             }
20773             
20774             if (!crow) {
20775                 crow = {
20776                     start : cells[i],
20777                     end :  cells[i]
20778                 };
20779                 continue;
20780             }
20781             if (crow.start.getY() == cells[i].getY()) {
20782                 // on same row.
20783                 crow.end = cells[i];
20784                 continue;
20785             }
20786             // different row.
20787             rows.push(crow);
20788             crow = {
20789                 start: cells[i],
20790                 end : cells[i]
20791             };
20792             
20793         }
20794         
20795         rows.push(crow);
20796         ev.els = [];
20797         ev.rows = rows;
20798         ev.cells = cells;
20799         
20800         cells[0].events.push(ev);
20801         
20802         this.calevents.push(ev);
20803     },
20804     
20805     clearEvents: function() {
20806         
20807         if(!this.calevents){
20808             return;
20809         }
20810         
20811         Roo.each(this.cells.elements, function(c){
20812             c.row = 0;
20813             c.events = [];
20814             c.more = [];
20815         });
20816         
20817         Roo.each(this.calevents, function(e) {
20818             Roo.each(e.els, function(el) {
20819                 el.un('mouseenter' ,this.onEventEnter, this);
20820                 el.un('mouseleave' ,this.onEventLeave, this);
20821                 el.remove();
20822             },this);
20823         },this);
20824         
20825         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20826             e.remove();
20827         });
20828         
20829     },
20830     
20831     renderEvents: function()
20832     {   
20833         var _this = this;
20834         
20835         this.cells.each(function(c) {
20836             
20837             if(c.row < 5){
20838                 return;
20839             }
20840             
20841             var ev = c.events;
20842             
20843             var r = 4;
20844             if(c.row != c.events.length){
20845                 r = 4 - (4 - (c.row - c.events.length));
20846             }
20847             
20848             c.events = ev.slice(0, r);
20849             c.more = ev.slice(r);
20850             
20851             if(c.more.length && c.more.length == 1){
20852                 c.events.push(c.more.pop());
20853             }
20854             
20855             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20856             
20857         });
20858             
20859         this.cells.each(function(c) {
20860             
20861             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20862             
20863             
20864             for (var e = 0; e < c.events.length; e++){
20865                 var ev = c.events[e];
20866                 var rows = ev.rows;
20867                 
20868                 for(var i = 0; i < rows.length; i++) {
20869                 
20870                     // how many rows should it span..
20871
20872                     var  cfg = {
20873                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20874                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20875
20876                         unselectable : "on",
20877                         cn : [
20878                             {
20879                                 cls: 'fc-event-inner',
20880                                 cn : [
20881     //                                {
20882     //                                  tag:'span',
20883     //                                  cls: 'fc-event-time',
20884     //                                  html : cells.length > 1 ? '' : ev.time
20885     //                                },
20886                                     {
20887                                       tag:'span',
20888                                       cls: 'fc-event-title',
20889                                       html : String.format('{0}', ev.title)
20890                                     }
20891
20892
20893                                 ]
20894                             },
20895                             {
20896                                 cls: 'ui-resizable-handle ui-resizable-e',
20897                                 html : '&nbsp;&nbsp;&nbsp'
20898                             }
20899
20900                         ]
20901                     };
20902
20903                     if (i == 0) {
20904                         cfg.cls += ' fc-event-start';
20905                     }
20906                     if ((i+1) == rows.length) {
20907                         cfg.cls += ' fc-event-end';
20908                     }
20909
20910                     var ctr = _this.el.select('.fc-event-container',true).first();
20911                     var cg = ctr.createChild(cfg);
20912
20913                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20914                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20915
20916                     var r = (c.more.length) ? 1 : 0;
20917                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20918                     cg.setWidth(ebox.right - sbox.x -2);
20919
20920                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20921                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20922                     cg.on('click', _this.onEventClick, _this, ev);
20923
20924                     ev.els.push(cg);
20925                     
20926                 }
20927                 
20928             }
20929             
20930             
20931             if(c.more.length){
20932                 var  cfg = {
20933                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20934                     style : 'position: absolute',
20935                     unselectable : "on",
20936                     cn : [
20937                         {
20938                             cls: 'fc-event-inner',
20939                             cn : [
20940                                 {
20941                                   tag:'span',
20942                                   cls: 'fc-event-title',
20943                                   html : 'More'
20944                                 }
20945
20946
20947                             ]
20948                         },
20949                         {
20950                             cls: 'ui-resizable-handle ui-resizable-e',
20951                             html : '&nbsp;&nbsp;&nbsp'
20952                         }
20953
20954                     ]
20955                 };
20956
20957                 var ctr = _this.el.select('.fc-event-container',true).first();
20958                 var cg = ctr.createChild(cfg);
20959
20960                 var sbox = c.select('.fc-day-content',true).first().getBox();
20961                 var ebox = c.select('.fc-day-content',true).first().getBox();
20962                 //Roo.log(cg);
20963                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20964                 cg.setWidth(ebox.right - sbox.x -2);
20965
20966                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20967                 
20968             }
20969             
20970         });
20971         
20972         
20973         
20974     },
20975     
20976     onEventEnter: function (e, el,event,d) {
20977         this.fireEvent('evententer', this, el, event);
20978     },
20979     
20980     onEventLeave: function (e, el,event,d) {
20981         this.fireEvent('eventleave', this, el, event);
20982     },
20983     
20984     onEventClick: function (e, el,event,d) {
20985         this.fireEvent('eventclick', this, el, event);
20986     },
20987     
20988     onMonthChange: function () {
20989         this.store.load();
20990     },
20991     
20992     onMoreEventClick: function(e, el, more)
20993     {
20994         var _this = this;
20995         
20996         this.calpopover.placement = 'right';
20997         this.calpopover.setTitle('More');
20998         
20999         this.calpopover.setContent('');
21000         
21001         var ctr = this.calpopover.el.select('.popover-content', true).first();
21002         
21003         Roo.each(more, function(m){
21004             var cfg = {
21005                 cls : 'fc-event-hori fc-event-draggable',
21006                 html : m.title
21007             };
21008             var cg = ctr.createChild(cfg);
21009             
21010             cg.on('click', _this.onEventClick, _this, m);
21011         });
21012         
21013         this.calpopover.show(el);
21014         
21015         
21016     },
21017     
21018     onLoad: function () 
21019     {   
21020         this.calevents = [];
21021         var cal = this;
21022         
21023         if(this.store.getCount() > 0){
21024             this.store.data.each(function(d){
21025                cal.addItem({
21026                     id : d.data.id,
21027                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21028                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21029                     time : d.data.start_time,
21030                     title : d.data.title,
21031                     description : d.data.description,
21032                     venue : d.data.venue
21033                 });
21034             });
21035         }
21036         
21037         this.renderEvents();
21038         
21039         if(this.calevents.length && this.loadMask){
21040             this.maskEl.hide();
21041         }
21042     },
21043     
21044     onBeforeLoad: function()
21045     {
21046         this.clearEvents();
21047         if(this.loadMask){
21048             this.maskEl.show();
21049         }
21050     }
21051 });
21052
21053  
21054  /*
21055  * - LGPL
21056  *
21057  * element
21058  * 
21059  */
21060
21061 /**
21062  * @class Roo.bootstrap.Popover
21063  * @extends Roo.bootstrap.Component
21064  * Bootstrap Popover class
21065  * @cfg {String} html contents of the popover   (or false to use children..)
21066  * @cfg {String} title of popover (or false to hide)
21067  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21068  * @cfg {String} trigger click || hover (or false to trigger manually)
21069  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21070  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21071  *      - if false and it has a 'parent' then it will be automatically added to that element
21072  *      - if string - Roo.get  will be called 
21073  * @cfg {Number} delay - delay before showing
21074  
21075  * @constructor
21076  * Create a new Popover
21077  * @param {Object} config The config object
21078  */
21079
21080 Roo.bootstrap.Popover = function(config){
21081     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21082     
21083     this.addEvents({
21084         // raw events
21085          /**
21086          * @event show
21087          * After the popover show
21088          * 
21089          * @param {Roo.bootstrap.Popover} this
21090          */
21091         "show" : true,
21092         /**
21093          * @event hide
21094          * After the popover hide
21095          * 
21096          * @param {Roo.bootstrap.Popover} this
21097          */
21098         "hide" : true
21099     });
21100 };
21101
21102 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21103     
21104     title: false,
21105     html: false,
21106     
21107     placement : 'right',
21108     trigger : 'hover', // hover
21109     modal : false,
21110     delay : 0,
21111     
21112     over: false,
21113     
21114     can_build_overlaid : false,
21115     
21116     maskEl : false, // the mask element
21117     headerEl : false,
21118     contentEl : false,
21119     alignEl : false, // when show is called with an element - this get's stored.
21120     
21121     getChildContainer : function()
21122     {
21123         return this.contentEl;
21124         
21125     },
21126     getPopoverHeader : function()
21127     {
21128         this.title = true; // flag not to hide it..
21129         this.headerEl.addClass('p-0');
21130         return this.headerEl
21131     },
21132     
21133     
21134     getAutoCreate : function(){
21135          
21136         var cfg = {
21137            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21138            style: 'display:block',
21139            cn : [
21140                 {
21141                     cls : 'arrow'
21142                 },
21143                 {
21144                     cls : 'popover-inner ',
21145                     cn : [
21146                         {
21147                             tag: 'h3',
21148                             cls: 'popover-title popover-header',
21149                             html : this.title === false ? '' : this.title
21150                         },
21151                         {
21152                             cls : 'popover-content popover-body '  + (this.cls || ''),
21153                             html : this.html || ''
21154                         }
21155                     ]
21156                     
21157                 }
21158            ]
21159         };
21160         
21161         return cfg;
21162     },
21163     /**
21164      * @param {string} the title
21165      */
21166     setTitle: function(str)
21167     {
21168         this.title = str;
21169         if (this.el) {
21170             this.headerEl.dom.innerHTML = str;
21171         }
21172         
21173     },
21174     /**
21175      * @param {string} the body content
21176      */
21177     setContent: function(str)
21178     {
21179         this.html = str;
21180         if (this.contentEl) {
21181             this.contentEl.dom.innerHTML = str;
21182         }
21183         
21184     },
21185     // as it get's added to the bottom of the page.
21186     onRender : function(ct, position)
21187     {
21188         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21189         
21190         
21191         
21192         if(!this.el){
21193             var cfg = Roo.apply({},  this.getAutoCreate());
21194             cfg.id = Roo.id();
21195             
21196             if (this.cls) {
21197                 cfg.cls += ' ' + this.cls;
21198             }
21199             if (this.style) {
21200                 cfg.style = this.style;
21201             }
21202             //Roo.log("adding to ");
21203             this.el = Roo.get(document.body).createChild(cfg, position);
21204 //            Roo.log(this.el);
21205         }
21206         
21207         this.contentEl = this.el.select('.popover-content',true).first();
21208         this.headerEl =  this.el.select('.popover-title',true).first();
21209         
21210         var nitems = [];
21211         if(typeof(this.items) != 'undefined'){
21212             var items = this.items;
21213             delete this.items;
21214
21215             for(var i =0;i < items.length;i++) {
21216                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21217             }
21218         }
21219
21220         this.items = nitems;
21221         
21222         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21223         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21224         
21225         
21226         
21227         this.initEvents();
21228     },
21229     
21230     resizeMask : function()
21231     {
21232         this.maskEl.setSize(
21233             Roo.lib.Dom.getViewWidth(true),
21234             Roo.lib.Dom.getViewHeight(true)
21235         );
21236     },
21237     
21238     initEvents : function()
21239     {
21240         
21241         if (!this.modal) { 
21242             Roo.bootstrap.Popover.register(this);
21243         }
21244          
21245         this.arrowEl = this.el.select('.arrow',true).first();
21246         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21247         this.el.enableDisplayMode('block');
21248         this.el.hide();
21249  
21250         
21251         if (this.over === false && !this.parent()) {
21252             return; 
21253         }
21254         if (this.triggers === false) {
21255             return;
21256         }
21257          
21258         // support parent
21259         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21260         var triggers = this.trigger ? this.trigger.split(' ') : [];
21261         Roo.each(triggers, function(trigger) {
21262         
21263             if (trigger == 'click') {
21264                 on_el.on('click', this.toggle, this);
21265             } else if (trigger != 'manual') {
21266                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21267                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21268       
21269                 on_el.on(eventIn  ,this.enter, this);
21270                 on_el.on(eventOut, this.leave, this);
21271             }
21272         }, this);
21273     },
21274     
21275     
21276     // private
21277     timeout : null,
21278     hoverState : null,
21279     
21280     toggle : function () {
21281         this.hoverState == 'in' ? this.leave() : this.enter();
21282     },
21283     
21284     enter : function () {
21285         
21286         clearTimeout(this.timeout);
21287     
21288         this.hoverState = 'in';
21289     
21290         if (!this.delay || !this.delay.show) {
21291             this.show();
21292             return;
21293         }
21294         var _t = this;
21295         this.timeout = setTimeout(function () {
21296             if (_t.hoverState == 'in') {
21297                 _t.show();
21298             }
21299         }, this.delay.show)
21300     },
21301     
21302     leave : function() {
21303         clearTimeout(this.timeout);
21304     
21305         this.hoverState = 'out';
21306     
21307         if (!this.delay || !this.delay.hide) {
21308             this.hide();
21309             return;
21310         }
21311         var _t = this;
21312         this.timeout = setTimeout(function () {
21313             if (_t.hoverState == 'out') {
21314                 _t.hide();
21315             }
21316         }, this.delay.hide)
21317     },
21318     /**
21319      * Show the popover
21320      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21321      * @param {string} (left|right|top|bottom) position
21322      */
21323     show : function (on_el, placement)
21324     {
21325         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21326         on_el = on_el || false; // default to false
21327          
21328         if (!on_el) {
21329             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21330                 on_el = this.parent().el;
21331             } else if (this.over) {
21332                 on_el = Roo.get(this.over);
21333             }
21334             
21335         }
21336         
21337         this.alignEl = Roo.get( on_el );
21338
21339         if (!this.el) {
21340             this.render(document.body);
21341         }
21342         
21343         
21344          
21345         
21346         if (this.title === false) {
21347             this.headerEl.hide();
21348         }
21349         
21350        
21351         this.el.show();
21352         this.el.dom.style.display = 'block';
21353          
21354  
21355         if (this.alignEl) {
21356             this.updatePosition(this.placement, true);
21357              
21358         } else {
21359             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21360             var es = this.el.getSize();
21361             var x = Roo.lib.Dom.getViewWidth()/2;
21362             var y = Roo.lib.Dom.getViewHeight()/2;
21363             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21364             
21365         }
21366
21367         
21368         //var arrow = this.el.select('.arrow',true).first();
21369         //arrow.set(align[2], 
21370         
21371         this.el.addClass('in');
21372         
21373          
21374         
21375         this.hoverState = 'in';
21376         
21377         if (this.modal) {
21378             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21379             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21380             this.maskEl.dom.style.display = 'block';
21381             this.maskEl.addClass('show');
21382         }
21383         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21384  
21385         this.fireEvent('show', this);
21386         
21387     },
21388     /**
21389      * fire this manually after loading a grid in the table for example
21390      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21391      * @param {Boolean} try and move it if we cant get right position.
21392      */
21393     updatePosition : function(placement, try_move)
21394     {
21395         // allow for calling with no parameters
21396         placement = placement   ? placement :  this.placement;
21397         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21398         
21399         this.el.removeClass([
21400             'fade','top','bottom', 'left', 'right','in',
21401             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21402         ]);
21403         this.el.addClass(placement + ' bs-popover-' + placement);
21404         
21405         if (!this.alignEl ) {
21406             return false;
21407         }
21408         
21409         switch (placement) {
21410             case 'right':
21411                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21412                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21413                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21414                     //normal display... or moved up/down.
21415                     this.el.setXY(offset);
21416                     var xy = this.alignEl.getAnchorXY('tr', false);
21417                     xy[0]+=2;xy[1]+=5;
21418                     this.arrowEl.setXY(xy);
21419                     return true;
21420                 }
21421                 // continue through...
21422                 return this.updatePosition('left', false);
21423                 
21424             
21425             case 'left':
21426                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21427                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21428                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21429                     //normal display... or moved up/down.
21430                     this.el.setXY(offset);
21431                     var xy = this.alignEl.getAnchorXY('tl', false);
21432                     xy[0]-=10;xy[1]+=5; // << fix me
21433                     this.arrowEl.setXY(xy);
21434                     return true;
21435                 }
21436                 // call self...
21437                 return this.updatePosition('right', false);
21438             
21439             case 'top':
21440                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21441                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21442                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21443                     //normal display... or moved up/down.
21444                     this.el.setXY(offset);
21445                     var xy = this.alignEl.getAnchorXY('t', false);
21446                     xy[1]-=10; // << fix me
21447                     this.arrowEl.setXY(xy);
21448                     return true;
21449                 }
21450                 // fall through
21451                return this.updatePosition('bottom', false);
21452             
21453             case 'bottom':
21454                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21455                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21456                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21457                     //normal display... or moved up/down.
21458                     this.el.setXY(offset);
21459                     var xy = this.alignEl.getAnchorXY('b', false);
21460                      xy[1]+=2; // << fix me
21461                     this.arrowEl.setXY(xy);
21462                     return true;
21463                 }
21464                 // fall through
21465                 return this.updatePosition('top', false);
21466                 
21467             
21468         }
21469         
21470         
21471         return false;
21472     },
21473     
21474     hide : function()
21475     {
21476         this.el.setXY([0,0]);
21477         this.el.removeClass('in');
21478         this.el.hide();
21479         this.hoverState = null;
21480         this.maskEl.hide(); // always..
21481         this.fireEvent('hide', this);
21482     }
21483     
21484 });
21485
21486
21487 Roo.apply(Roo.bootstrap.Popover, {
21488
21489     alignment : {
21490         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21491         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21492         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21493         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21494     },
21495     
21496     zIndex : 20001,
21497
21498     clickHander : false,
21499     
21500     
21501
21502     onMouseDown : function(e)
21503     {
21504         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21505             /// what is nothing is showing..
21506             this.hideAll();
21507         }
21508          
21509     },
21510     
21511     
21512     popups : [],
21513     
21514     register : function(popup)
21515     {
21516         if (!Roo.bootstrap.Popover.clickHandler) {
21517             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21518         }
21519         // hide other popups.
21520         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21521         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21522         this.hideAll(); //<< why?
21523         //this.popups.push(popup);
21524     },
21525     hideAll : function()
21526     {
21527         this.popups.forEach(function(p) {
21528             p.hide();
21529         });
21530     },
21531     onShow : function() {
21532         Roo.bootstrap.Popover.popups.push(this);
21533     },
21534     onHide : function() {
21535         Roo.bootstrap.Popover.popups.remove(this);
21536     } 
21537
21538 });/*
21539  * - LGPL
21540  *
21541  * Card header - holder for the card header elements.
21542  * 
21543  */
21544
21545 /**
21546  * @class Roo.bootstrap.PopoverNav
21547  * @extends Roo.bootstrap.NavGroup
21548  * Bootstrap Popover header navigation class
21549  * @constructor
21550  * Create a new Popover Header Navigation 
21551  * @param {Object} config The config object
21552  */
21553
21554 Roo.bootstrap.PopoverNav = function(config){
21555     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21556 };
21557
21558 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21559     
21560     
21561     container_method : 'getPopoverHeader' 
21562     
21563      
21564     
21565     
21566    
21567 });
21568
21569  
21570
21571  /*
21572  * - LGPL
21573  *
21574  * Progress
21575  * 
21576  */
21577
21578 /**
21579  * @class Roo.bootstrap.Progress
21580  * @extends Roo.bootstrap.Component
21581  * Bootstrap Progress class
21582  * @cfg {Boolean} striped striped of the progress bar
21583  * @cfg {Boolean} active animated of the progress bar
21584  * 
21585  * 
21586  * @constructor
21587  * Create a new Progress
21588  * @param {Object} config The config object
21589  */
21590
21591 Roo.bootstrap.Progress = function(config){
21592     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21593 };
21594
21595 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21596     
21597     striped : false,
21598     active: false,
21599     
21600     getAutoCreate : function(){
21601         var cfg = {
21602             tag: 'div',
21603             cls: 'progress'
21604         };
21605         
21606         
21607         if(this.striped){
21608             cfg.cls += ' progress-striped';
21609         }
21610       
21611         if(this.active){
21612             cfg.cls += ' active';
21613         }
21614         
21615         
21616         return cfg;
21617     }
21618    
21619 });
21620
21621  
21622
21623  /*
21624  * - LGPL
21625  *
21626  * ProgressBar
21627  * 
21628  */
21629
21630 /**
21631  * @class Roo.bootstrap.ProgressBar
21632  * @extends Roo.bootstrap.Component
21633  * Bootstrap ProgressBar class
21634  * @cfg {Number} aria_valuenow aria-value now
21635  * @cfg {Number} aria_valuemin aria-value min
21636  * @cfg {Number} aria_valuemax aria-value max
21637  * @cfg {String} label label for the progress bar
21638  * @cfg {String} panel (success | info | warning | danger )
21639  * @cfg {String} role role of the progress bar
21640  * @cfg {String} sr_only text
21641  * 
21642  * 
21643  * @constructor
21644  * Create a new ProgressBar
21645  * @param {Object} config The config object
21646  */
21647
21648 Roo.bootstrap.ProgressBar = function(config){
21649     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21650 };
21651
21652 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21653     
21654     aria_valuenow : 0,
21655     aria_valuemin : 0,
21656     aria_valuemax : 100,
21657     label : false,
21658     panel : false,
21659     role : false,
21660     sr_only: false,
21661     
21662     getAutoCreate : function()
21663     {
21664         
21665         var cfg = {
21666             tag: 'div',
21667             cls: 'progress-bar',
21668             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21669         };
21670         
21671         if(this.sr_only){
21672             cfg.cn = {
21673                 tag: 'span',
21674                 cls: 'sr-only',
21675                 html: this.sr_only
21676             }
21677         }
21678         
21679         if(this.role){
21680             cfg.role = this.role;
21681         }
21682         
21683         if(this.aria_valuenow){
21684             cfg['aria-valuenow'] = this.aria_valuenow;
21685         }
21686         
21687         if(this.aria_valuemin){
21688             cfg['aria-valuemin'] = this.aria_valuemin;
21689         }
21690         
21691         if(this.aria_valuemax){
21692             cfg['aria-valuemax'] = this.aria_valuemax;
21693         }
21694         
21695         if(this.label && !this.sr_only){
21696             cfg.html = this.label;
21697         }
21698         
21699         if(this.panel){
21700             cfg.cls += ' progress-bar-' + this.panel;
21701         }
21702         
21703         return cfg;
21704     },
21705     
21706     update : function(aria_valuenow)
21707     {
21708         this.aria_valuenow = aria_valuenow;
21709         
21710         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21711     }
21712    
21713 });
21714
21715  
21716
21717  /*
21718  * - LGPL
21719  *
21720  * column
21721  * 
21722  */
21723
21724 /**
21725  * @class Roo.bootstrap.TabGroup
21726  * @extends Roo.bootstrap.Column
21727  * Bootstrap Column class
21728  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21729  * @cfg {Boolean} carousel true to make the group behave like a carousel
21730  * @cfg {Boolean} bullets show bullets for the panels
21731  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21732  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21733  * @cfg {Boolean} showarrow (true|false) show arrow default true
21734  * 
21735  * @constructor
21736  * Create a new TabGroup
21737  * @param {Object} config The config object
21738  */
21739
21740 Roo.bootstrap.TabGroup = function(config){
21741     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21742     if (!this.navId) {
21743         this.navId = Roo.id();
21744     }
21745     this.tabs = [];
21746     Roo.bootstrap.TabGroup.register(this);
21747     
21748 };
21749
21750 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21751     
21752     carousel : false,
21753     transition : false,
21754     bullets : 0,
21755     timer : 0,
21756     autoslide : false,
21757     slideFn : false,
21758     slideOnTouch : false,
21759     showarrow : true,
21760     
21761     getAutoCreate : function()
21762     {
21763         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21764         
21765         cfg.cls += ' tab-content';
21766         
21767         if (this.carousel) {
21768             cfg.cls += ' carousel slide';
21769             
21770             cfg.cn = [{
21771                cls : 'carousel-inner',
21772                cn : []
21773             }];
21774         
21775             if(this.bullets  && !Roo.isTouch){
21776                 
21777                 var bullets = {
21778                     cls : 'carousel-bullets',
21779                     cn : []
21780                 };
21781                
21782                 if(this.bullets_cls){
21783                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21784                 }
21785                 
21786                 bullets.cn.push({
21787                     cls : 'clear'
21788                 });
21789                 
21790                 cfg.cn[0].cn.push(bullets);
21791             }
21792             
21793             if(this.showarrow){
21794                 cfg.cn[0].cn.push({
21795                     tag : 'div',
21796                     class : 'carousel-arrow',
21797                     cn : [
21798                         {
21799                             tag : 'div',
21800                             class : 'carousel-prev',
21801                             cn : [
21802                                 {
21803                                     tag : 'i',
21804                                     class : 'fa fa-chevron-left'
21805                                 }
21806                             ]
21807                         },
21808                         {
21809                             tag : 'div',
21810                             class : 'carousel-next',
21811                             cn : [
21812                                 {
21813                                     tag : 'i',
21814                                     class : 'fa fa-chevron-right'
21815                                 }
21816                             ]
21817                         }
21818                     ]
21819                 });
21820             }
21821             
21822         }
21823         
21824         return cfg;
21825     },
21826     
21827     initEvents:  function()
21828     {
21829 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21830 //            this.el.on("touchstart", this.onTouchStart, this);
21831 //        }
21832         
21833         if(this.autoslide){
21834             var _this = this;
21835             
21836             this.slideFn = window.setInterval(function() {
21837                 _this.showPanelNext();
21838             }, this.timer);
21839         }
21840         
21841         if(this.showarrow){
21842             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21843             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21844         }
21845         
21846         
21847     },
21848     
21849 //    onTouchStart : function(e, el, o)
21850 //    {
21851 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21852 //            return;
21853 //        }
21854 //        
21855 //        this.showPanelNext();
21856 //    },
21857     
21858     
21859     getChildContainer : function()
21860     {
21861         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21862     },
21863     
21864     /**
21865     * register a Navigation item
21866     * @param {Roo.bootstrap.NavItem} the navitem to add
21867     */
21868     register : function(item)
21869     {
21870         this.tabs.push( item);
21871         item.navId = this.navId; // not really needed..
21872         this.addBullet();
21873     
21874     },
21875     
21876     getActivePanel : function()
21877     {
21878         var r = false;
21879         Roo.each(this.tabs, function(t) {
21880             if (t.active) {
21881                 r = t;
21882                 return false;
21883             }
21884             return null;
21885         });
21886         return r;
21887         
21888     },
21889     getPanelByName : function(n)
21890     {
21891         var r = false;
21892         Roo.each(this.tabs, function(t) {
21893             if (t.tabId == n) {
21894                 r = t;
21895                 return false;
21896             }
21897             return null;
21898         });
21899         return r;
21900     },
21901     indexOfPanel : function(p)
21902     {
21903         var r = false;
21904         Roo.each(this.tabs, function(t,i) {
21905             if (t.tabId == p.tabId) {
21906                 r = i;
21907                 return false;
21908             }
21909             return null;
21910         });
21911         return r;
21912     },
21913     /**
21914      * show a specific panel
21915      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21916      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21917      */
21918     showPanel : function (pan)
21919     {
21920         if(this.transition || typeof(pan) == 'undefined'){
21921             Roo.log("waiting for the transitionend");
21922             return false;
21923         }
21924         
21925         if (typeof(pan) == 'number') {
21926             pan = this.tabs[pan];
21927         }
21928         
21929         if (typeof(pan) == 'string') {
21930             pan = this.getPanelByName(pan);
21931         }
21932         
21933         var cur = this.getActivePanel();
21934         
21935         if(!pan || !cur){
21936             Roo.log('pan or acitve pan is undefined');
21937             return false;
21938         }
21939         
21940         if (pan.tabId == this.getActivePanel().tabId) {
21941             return true;
21942         }
21943         
21944         if (false === cur.fireEvent('beforedeactivate')) {
21945             return false;
21946         }
21947         
21948         if(this.bullets > 0 && !Roo.isTouch){
21949             this.setActiveBullet(this.indexOfPanel(pan));
21950         }
21951         
21952         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21953             
21954             //class="carousel-item carousel-item-next carousel-item-left"
21955             
21956             this.transition = true;
21957             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21958             var lr = dir == 'next' ? 'left' : 'right';
21959             pan.el.addClass(dir); // or prev
21960             pan.el.addClass('carousel-item-' + dir); // or prev
21961             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21962             cur.el.addClass(lr); // or right
21963             pan.el.addClass(lr);
21964             cur.el.addClass('carousel-item-' +lr); // or right
21965             pan.el.addClass('carousel-item-' +lr);
21966             
21967             
21968             var _this = this;
21969             cur.el.on('transitionend', function() {
21970                 Roo.log("trans end?");
21971                 
21972                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21973                 pan.setActive(true);
21974                 
21975                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21976                 cur.setActive(false);
21977                 
21978                 _this.transition = false;
21979                 
21980             }, this, { single:  true } );
21981             
21982             return true;
21983         }
21984         
21985         cur.setActive(false);
21986         pan.setActive(true);
21987         
21988         return true;
21989         
21990     },
21991     showPanelNext : function()
21992     {
21993         var i = this.indexOfPanel(this.getActivePanel());
21994         
21995         if (i >= this.tabs.length - 1 && !this.autoslide) {
21996             return;
21997         }
21998         
21999         if (i >= this.tabs.length - 1 && this.autoslide) {
22000             i = -1;
22001         }
22002         
22003         this.showPanel(this.tabs[i+1]);
22004     },
22005     
22006     showPanelPrev : function()
22007     {
22008         var i = this.indexOfPanel(this.getActivePanel());
22009         
22010         if (i  < 1 && !this.autoslide) {
22011             return;
22012         }
22013         
22014         if (i < 1 && this.autoslide) {
22015             i = this.tabs.length;
22016         }
22017         
22018         this.showPanel(this.tabs[i-1]);
22019     },
22020     
22021     
22022     addBullet: function()
22023     {
22024         if(!this.bullets || Roo.isTouch){
22025             return;
22026         }
22027         var ctr = this.el.select('.carousel-bullets',true).first();
22028         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22029         var bullet = ctr.createChild({
22030             cls : 'bullet bullet-' + i
22031         },ctr.dom.lastChild);
22032         
22033         
22034         var _this = this;
22035         
22036         bullet.on('click', (function(e, el, o, ii, t){
22037
22038             e.preventDefault();
22039
22040             this.showPanel(ii);
22041
22042             if(this.autoslide && this.slideFn){
22043                 clearInterval(this.slideFn);
22044                 this.slideFn = window.setInterval(function() {
22045                     _this.showPanelNext();
22046                 }, this.timer);
22047             }
22048
22049         }).createDelegate(this, [i, bullet], true));
22050                 
22051         
22052     },
22053      
22054     setActiveBullet : function(i)
22055     {
22056         if(Roo.isTouch){
22057             return;
22058         }
22059         
22060         Roo.each(this.el.select('.bullet', true).elements, function(el){
22061             el.removeClass('selected');
22062         });
22063
22064         var bullet = this.el.select('.bullet-' + i, true).first();
22065         
22066         if(!bullet){
22067             return;
22068         }
22069         
22070         bullet.addClass('selected');
22071     }
22072     
22073     
22074   
22075 });
22076
22077  
22078
22079  
22080  
22081 Roo.apply(Roo.bootstrap.TabGroup, {
22082     
22083     groups: {},
22084      /**
22085     * register a Navigation Group
22086     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22087     */
22088     register : function(navgrp)
22089     {
22090         this.groups[navgrp.navId] = navgrp;
22091         
22092     },
22093     /**
22094     * fetch a Navigation Group based on the navigation ID
22095     * if one does not exist , it will get created.
22096     * @param {string} the navgroup to add
22097     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22098     */
22099     get: function(navId) {
22100         if (typeof(this.groups[navId]) == 'undefined') {
22101             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22102         }
22103         return this.groups[navId] ;
22104     }
22105     
22106     
22107     
22108 });
22109
22110  /*
22111  * - LGPL
22112  *
22113  * TabPanel
22114  * 
22115  */
22116
22117 /**
22118  * @class Roo.bootstrap.TabPanel
22119  * @extends Roo.bootstrap.Component
22120  * Bootstrap TabPanel class
22121  * @cfg {Boolean} active panel active
22122  * @cfg {String} html panel content
22123  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22124  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22125  * @cfg {String} href click to link..
22126  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22127  * 
22128  * 
22129  * @constructor
22130  * Create a new TabPanel
22131  * @param {Object} config The config object
22132  */
22133
22134 Roo.bootstrap.TabPanel = function(config){
22135     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22136     this.addEvents({
22137         /**
22138              * @event changed
22139              * Fires when the active status changes
22140              * @param {Roo.bootstrap.TabPanel} this
22141              * @param {Boolean} state the new state
22142             
22143          */
22144         'changed': true,
22145         /**
22146              * @event beforedeactivate
22147              * Fires before a tab is de-activated - can be used to do validation on a form.
22148              * @param {Roo.bootstrap.TabPanel} this
22149              * @return {Boolean} false if there is an error
22150             
22151          */
22152         'beforedeactivate': true
22153      });
22154     
22155     this.tabId = this.tabId || Roo.id();
22156   
22157 };
22158
22159 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22160     
22161     active: false,
22162     html: false,
22163     tabId: false,
22164     navId : false,
22165     href : '',
22166     touchSlide : false,
22167     getAutoCreate : function(){
22168         
22169         
22170         var cfg = {
22171             tag: 'div',
22172             // item is needed for carousel - not sure if it has any effect otherwise
22173             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22174             html: this.html || ''
22175         };
22176         
22177         if(this.active){
22178             cfg.cls += ' active';
22179         }
22180         
22181         if(this.tabId){
22182             cfg.tabId = this.tabId;
22183         }
22184         
22185         
22186         
22187         return cfg;
22188     },
22189     
22190     initEvents:  function()
22191     {
22192         var p = this.parent();
22193         
22194         this.navId = this.navId || p.navId;
22195         
22196         if (typeof(this.navId) != 'undefined') {
22197             // not really needed.. but just in case.. parent should be a NavGroup.
22198             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22199             
22200             tg.register(this);
22201             
22202             var i = tg.tabs.length - 1;
22203             
22204             if(this.active && tg.bullets > 0 && i < tg.bullets){
22205                 tg.setActiveBullet(i);
22206             }
22207         }
22208         
22209         this.el.on('click', this.onClick, this);
22210         
22211         if(Roo.isTouch && this.touchSlide){
22212             this.el.on("touchstart", this.onTouchStart, this);
22213             this.el.on("touchmove", this.onTouchMove, this);
22214             this.el.on("touchend", this.onTouchEnd, this);
22215         }
22216         
22217     },
22218     
22219     onRender : function(ct, position)
22220     {
22221         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22222     },
22223     
22224     setActive : function(state)
22225     {
22226         Roo.log("panel - set active " + this.tabId + "=" + state);
22227         
22228         this.active = state;
22229         if (!state) {
22230             this.el.removeClass('active');
22231             
22232         } else  if (!this.el.hasClass('active')) {
22233             this.el.addClass('active');
22234         }
22235         
22236         this.fireEvent('changed', this, state);
22237     },
22238     
22239     onClick : function(e)
22240     {
22241         e.preventDefault();
22242         
22243         if(!this.href.length){
22244             return;
22245         }
22246         
22247         window.location.href = this.href;
22248     },
22249     
22250     startX : 0,
22251     startY : 0,
22252     endX : 0,
22253     endY : 0,
22254     swiping : false,
22255     
22256     onTouchStart : function(e)
22257     {
22258         this.swiping = false;
22259         
22260         this.startX = e.browserEvent.touches[0].clientX;
22261         this.startY = e.browserEvent.touches[0].clientY;
22262     },
22263     
22264     onTouchMove : function(e)
22265     {
22266         this.swiping = true;
22267         
22268         this.endX = e.browserEvent.touches[0].clientX;
22269         this.endY = e.browserEvent.touches[0].clientY;
22270     },
22271     
22272     onTouchEnd : function(e)
22273     {
22274         if(!this.swiping){
22275             this.onClick(e);
22276             return;
22277         }
22278         
22279         var tabGroup = this.parent();
22280         
22281         if(this.endX > this.startX){ // swiping right
22282             tabGroup.showPanelPrev();
22283             return;
22284         }
22285         
22286         if(this.startX > this.endX){ // swiping left
22287             tabGroup.showPanelNext();
22288             return;
22289         }
22290     }
22291     
22292     
22293 });
22294  
22295
22296  
22297
22298  /*
22299  * - LGPL
22300  *
22301  * DateField
22302  * 
22303  */
22304
22305 /**
22306  * @class Roo.bootstrap.DateField
22307  * @extends Roo.bootstrap.Input
22308  * Bootstrap DateField class
22309  * @cfg {Number} weekStart default 0
22310  * @cfg {String} viewMode default empty, (months|years)
22311  * @cfg {String} minViewMode default empty, (months|years)
22312  * @cfg {Number} startDate default -Infinity
22313  * @cfg {Number} endDate default Infinity
22314  * @cfg {Boolean} todayHighlight default false
22315  * @cfg {Boolean} todayBtn default false
22316  * @cfg {Boolean} calendarWeeks default false
22317  * @cfg {Object} daysOfWeekDisabled default empty
22318  * @cfg {Boolean} singleMode default false (true | false)
22319  * 
22320  * @cfg {Boolean} keyboardNavigation default true
22321  * @cfg {String} language default en
22322  * 
22323  * @constructor
22324  * Create a new DateField
22325  * @param {Object} config The config object
22326  */
22327
22328 Roo.bootstrap.DateField = function(config){
22329     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22330      this.addEvents({
22331             /**
22332              * @event show
22333              * Fires when this field show.
22334              * @param {Roo.bootstrap.DateField} this
22335              * @param {Mixed} date The date value
22336              */
22337             show : true,
22338             /**
22339              * @event show
22340              * Fires when this field hide.
22341              * @param {Roo.bootstrap.DateField} this
22342              * @param {Mixed} date The date value
22343              */
22344             hide : true,
22345             /**
22346              * @event select
22347              * Fires when select a date.
22348              * @param {Roo.bootstrap.DateField} this
22349              * @param {Mixed} date The date value
22350              */
22351             select : true,
22352             /**
22353              * @event beforeselect
22354              * Fires when before select a date.
22355              * @param {Roo.bootstrap.DateField} this
22356              * @param {Mixed} date The date value
22357              */
22358             beforeselect : true
22359         });
22360 };
22361
22362 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22363     
22364     /**
22365      * @cfg {String} format
22366      * The default date format string which can be overriden for localization support.  The format must be
22367      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22368      */
22369     format : "m/d/y",
22370     /**
22371      * @cfg {String} altFormats
22372      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22373      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22374      */
22375     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22376     
22377     weekStart : 0,
22378     
22379     viewMode : '',
22380     
22381     minViewMode : '',
22382     
22383     todayHighlight : false,
22384     
22385     todayBtn: false,
22386     
22387     language: 'en',
22388     
22389     keyboardNavigation: true,
22390     
22391     calendarWeeks: false,
22392     
22393     startDate: -Infinity,
22394     
22395     endDate: Infinity,
22396     
22397     daysOfWeekDisabled: [],
22398     
22399     _events: [],
22400     
22401     singleMode : false,
22402     
22403     UTCDate: function()
22404     {
22405         return new Date(Date.UTC.apply(Date, arguments));
22406     },
22407     
22408     UTCToday: function()
22409     {
22410         var today = new Date();
22411         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22412     },
22413     
22414     getDate: function() {
22415             var d = this.getUTCDate();
22416             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22417     },
22418     
22419     getUTCDate: function() {
22420             return this.date;
22421     },
22422     
22423     setDate: function(d) {
22424             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22425     },
22426     
22427     setUTCDate: function(d) {
22428             this.date = d;
22429             this.setValue(this.formatDate(this.date));
22430     },
22431         
22432     onRender: function(ct, position)
22433     {
22434         
22435         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22436         
22437         this.language = this.language || 'en';
22438         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22439         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22440         
22441         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22442         this.format = this.format || 'm/d/y';
22443         this.isInline = false;
22444         this.isInput = true;
22445         this.component = this.el.select('.add-on', true).first() || false;
22446         this.component = (this.component && this.component.length === 0) ? false : this.component;
22447         this.hasInput = this.component && this.inputEl().length;
22448         
22449         if (typeof(this.minViewMode === 'string')) {
22450             switch (this.minViewMode) {
22451                 case 'months':
22452                     this.minViewMode = 1;
22453                     break;
22454                 case 'years':
22455                     this.minViewMode = 2;
22456                     break;
22457                 default:
22458                     this.minViewMode = 0;
22459                     break;
22460             }
22461         }
22462         
22463         if (typeof(this.viewMode === 'string')) {
22464             switch (this.viewMode) {
22465                 case 'months':
22466                     this.viewMode = 1;
22467                     break;
22468                 case 'years':
22469                     this.viewMode = 2;
22470                     break;
22471                 default:
22472                     this.viewMode = 0;
22473                     break;
22474             }
22475         }
22476                 
22477         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22478         
22479 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22480         
22481         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22482         
22483         this.picker().on('mousedown', this.onMousedown, this);
22484         this.picker().on('click', this.onClick, this);
22485         
22486         this.picker().addClass('datepicker-dropdown');
22487         
22488         this.startViewMode = this.viewMode;
22489         
22490         if(this.singleMode){
22491             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22492                 v.setVisibilityMode(Roo.Element.DISPLAY);
22493                 v.hide();
22494             });
22495             
22496             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22497                 v.setStyle('width', '189px');
22498             });
22499         }
22500         
22501         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22502             if(!this.calendarWeeks){
22503                 v.remove();
22504                 return;
22505             }
22506             
22507             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22508             v.attr('colspan', function(i, val){
22509                 return parseInt(val) + 1;
22510             });
22511         });
22512                         
22513         
22514         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22515         
22516         this.setStartDate(this.startDate);
22517         this.setEndDate(this.endDate);
22518         
22519         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22520         
22521         this.fillDow();
22522         this.fillMonths();
22523         this.update();
22524         this.showMode();
22525         
22526         if(this.isInline) {
22527             this.showPopup();
22528         }
22529     },
22530     
22531     picker : function()
22532     {
22533         return this.pickerEl;
22534 //        return this.el.select('.datepicker', true).first();
22535     },
22536     
22537     fillDow: function()
22538     {
22539         var dowCnt = this.weekStart;
22540         
22541         var dow = {
22542             tag: 'tr',
22543             cn: [
22544                 
22545             ]
22546         };
22547         
22548         if(this.calendarWeeks){
22549             dow.cn.push({
22550                 tag: 'th',
22551                 cls: 'cw',
22552                 html: '&nbsp;'
22553             })
22554         }
22555         
22556         while (dowCnt < this.weekStart + 7) {
22557             dow.cn.push({
22558                 tag: 'th',
22559                 cls: 'dow',
22560                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22561             });
22562         }
22563         
22564         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22565     },
22566     
22567     fillMonths: function()
22568     {    
22569         var i = 0;
22570         var months = this.picker().select('>.datepicker-months td', true).first();
22571         
22572         months.dom.innerHTML = '';
22573         
22574         while (i < 12) {
22575             var month = {
22576                 tag: 'span',
22577                 cls: 'month',
22578                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22579             };
22580             
22581             months.createChild(month);
22582         }
22583         
22584     },
22585     
22586     update: function()
22587     {
22588         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;
22589         
22590         if (this.date < this.startDate) {
22591             this.viewDate = new Date(this.startDate);
22592         } else if (this.date > this.endDate) {
22593             this.viewDate = new Date(this.endDate);
22594         } else {
22595             this.viewDate = new Date(this.date);
22596         }
22597         
22598         this.fill();
22599     },
22600     
22601     fill: function() 
22602     {
22603         var d = new Date(this.viewDate),
22604                 year = d.getUTCFullYear(),
22605                 month = d.getUTCMonth(),
22606                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22607                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22608                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22609                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22610                 currentDate = this.date && this.date.valueOf(),
22611                 today = this.UTCToday();
22612         
22613         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22614         
22615 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22616         
22617 //        this.picker.select('>tfoot th.today').
22618 //                                              .text(dates[this.language].today)
22619 //                                              .toggle(this.todayBtn !== false);
22620     
22621         this.updateNavArrows();
22622         this.fillMonths();
22623                                                 
22624         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22625         
22626         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22627          
22628         prevMonth.setUTCDate(day);
22629         
22630         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22631         
22632         var nextMonth = new Date(prevMonth);
22633         
22634         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22635         
22636         nextMonth = nextMonth.valueOf();
22637         
22638         var fillMonths = false;
22639         
22640         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22641         
22642         while(prevMonth.valueOf() <= nextMonth) {
22643             var clsName = '';
22644             
22645             if (prevMonth.getUTCDay() === this.weekStart) {
22646                 if(fillMonths){
22647                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22648                 }
22649                     
22650                 fillMonths = {
22651                     tag: 'tr',
22652                     cn: []
22653                 };
22654                 
22655                 if(this.calendarWeeks){
22656                     // ISO 8601: First week contains first thursday.
22657                     // ISO also states week starts on Monday, but we can be more abstract here.
22658                     var
22659                     // Start of current week: based on weekstart/current date
22660                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22661                     // Thursday of this week
22662                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22663                     // First Thursday of year, year from thursday
22664                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22665                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22666                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22667                     
22668                     fillMonths.cn.push({
22669                         tag: 'td',
22670                         cls: 'cw',
22671                         html: calWeek
22672                     });
22673                 }
22674             }
22675             
22676             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22677                 clsName += ' old';
22678             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22679                 clsName += ' new';
22680             }
22681             if (this.todayHighlight &&
22682                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22683                 prevMonth.getUTCMonth() == today.getMonth() &&
22684                 prevMonth.getUTCDate() == today.getDate()) {
22685                 clsName += ' today';
22686             }
22687             
22688             if (currentDate && prevMonth.valueOf() === currentDate) {
22689                 clsName += ' active';
22690             }
22691             
22692             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22693                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22694                     clsName += ' disabled';
22695             }
22696             
22697             fillMonths.cn.push({
22698                 tag: 'td',
22699                 cls: 'day ' + clsName,
22700                 html: prevMonth.getDate()
22701             });
22702             
22703             prevMonth.setDate(prevMonth.getDate()+1);
22704         }
22705           
22706         var currentYear = this.date && this.date.getUTCFullYear();
22707         var currentMonth = this.date && this.date.getUTCMonth();
22708         
22709         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22710         
22711         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22712             v.removeClass('active');
22713             
22714             if(currentYear === year && k === currentMonth){
22715                 v.addClass('active');
22716             }
22717             
22718             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22719                 v.addClass('disabled');
22720             }
22721             
22722         });
22723         
22724         
22725         year = parseInt(year/10, 10) * 10;
22726         
22727         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22728         
22729         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22730         
22731         year -= 1;
22732         for (var i = -1; i < 11; i++) {
22733             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22734                 tag: 'span',
22735                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22736                 html: year
22737             });
22738             
22739             year += 1;
22740         }
22741     },
22742     
22743     showMode: function(dir) 
22744     {
22745         if (dir) {
22746             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22747         }
22748         
22749         Roo.each(this.picker().select('>div',true).elements, function(v){
22750             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22751             v.hide();
22752         });
22753         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22754     },
22755     
22756     place: function()
22757     {
22758         if(this.isInline) {
22759             return;
22760         }
22761         
22762         this.picker().removeClass(['bottom', 'top']);
22763         
22764         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22765             /*
22766              * place to the top of element!
22767              *
22768              */
22769             
22770             this.picker().addClass('top');
22771             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22772             
22773             return;
22774         }
22775         
22776         this.picker().addClass('bottom');
22777         
22778         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22779     },
22780     
22781     parseDate : function(value)
22782     {
22783         if(!value || value instanceof Date){
22784             return value;
22785         }
22786         var v = Date.parseDate(value, this.format);
22787         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22788             v = Date.parseDate(value, 'Y-m-d');
22789         }
22790         if(!v && this.altFormats){
22791             if(!this.altFormatsArray){
22792                 this.altFormatsArray = this.altFormats.split("|");
22793             }
22794             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22795                 v = Date.parseDate(value, this.altFormatsArray[i]);
22796             }
22797         }
22798         return v;
22799     },
22800     
22801     formatDate : function(date, fmt)
22802     {   
22803         return (!date || !(date instanceof Date)) ?
22804         date : date.dateFormat(fmt || this.format);
22805     },
22806     
22807     onFocus : function()
22808     {
22809         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22810         this.showPopup();
22811     },
22812     
22813     onBlur : function()
22814     {
22815         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22816         
22817         var d = this.inputEl().getValue();
22818         
22819         this.setValue(d);
22820                 
22821         this.hidePopup();
22822     },
22823     
22824     showPopup : function()
22825     {
22826         this.picker().show();
22827         this.update();
22828         this.place();
22829         
22830         this.fireEvent('showpopup', this, this.date);
22831     },
22832     
22833     hidePopup : function()
22834     {
22835         if(this.isInline) {
22836             return;
22837         }
22838         this.picker().hide();
22839         this.viewMode = this.startViewMode;
22840         this.showMode();
22841         
22842         this.fireEvent('hidepopup', this, this.date);
22843         
22844     },
22845     
22846     onMousedown: function(e)
22847     {
22848         e.stopPropagation();
22849         e.preventDefault();
22850     },
22851     
22852     keyup: function(e)
22853     {
22854         Roo.bootstrap.DateField.superclass.keyup.call(this);
22855         this.update();
22856     },
22857
22858     setValue: function(v)
22859     {
22860         if(this.fireEvent('beforeselect', this, v) !== false){
22861             var d = new Date(this.parseDate(v) ).clearTime();
22862         
22863             if(isNaN(d.getTime())){
22864                 this.date = this.viewDate = '';
22865                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22866                 return;
22867             }
22868
22869             v = this.formatDate(d);
22870
22871             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22872
22873             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22874
22875             this.update();
22876
22877             this.fireEvent('select', this, this.date);
22878         }
22879     },
22880     
22881     getValue: function()
22882     {
22883         return this.formatDate(this.date);
22884     },
22885     
22886     fireKey: function(e)
22887     {
22888         if (!this.picker().isVisible()){
22889             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22890                 this.showPopup();
22891             }
22892             return;
22893         }
22894         
22895         var dateChanged = false,
22896         dir, day, month,
22897         newDate, newViewDate;
22898         
22899         switch(e.keyCode){
22900             case 27: // escape
22901                 this.hidePopup();
22902                 e.preventDefault();
22903                 break;
22904             case 37: // left
22905             case 39: // right
22906                 if (!this.keyboardNavigation) {
22907                     break;
22908                 }
22909                 dir = e.keyCode == 37 ? -1 : 1;
22910                 
22911                 if (e.ctrlKey){
22912                     newDate = this.moveYear(this.date, dir);
22913                     newViewDate = this.moveYear(this.viewDate, dir);
22914                 } else if (e.shiftKey){
22915                     newDate = this.moveMonth(this.date, dir);
22916                     newViewDate = this.moveMonth(this.viewDate, dir);
22917                 } else {
22918                     newDate = new Date(this.date);
22919                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22920                     newViewDate = new Date(this.viewDate);
22921                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22922                 }
22923                 if (this.dateWithinRange(newDate)){
22924                     this.date = newDate;
22925                     this.viewDate = newViewDate;
22926                     this.setValue(this.formatDate(this.date));
22927 //                    this.update();
22928                     e.preventDefault();
22929                     dateChanged = true;
22930                 }
22931                 break;
22932             case 38: // up
22933             case 40: // down
22934                 if (!this.keyboardNavigation) {
22935                     break;
22936                 }
22937                 dir = e.keyCode == 38 ? -1 : 1;
22938                 if (e.ctrlKey){
22939                     newDate = this.moveYear(this.date, dir);
22940                     newViewDate = this.moveYear(this.viewDate, dir);
22941                 } else if (e.shiftKey){
22942                     newDate = this.moveMonth(this.date, dir);
22943                     newViewDate = this.moveMonth(this.viewDate, dir);
22944                 } else {
22945                     newDate = new Date(this.date);
22946                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22947                     newViewDate = new Date(this.viewDate);
22948                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22949                 }
22950                 if (this.dateWithinRange(newDate)){
22951                     this.date = newDate;
22952                     this.viewDate = newViewDate;
22953                     this.setValue(this.formatDate(this.date));
22954 //                    this.update();
22955                     e.preventDefault();
22956                     dateChanged = true;
22957                 }
22958                 break;
22959             case 13: // enter
22960                 this.setValue(this.formatDate(this.date));
22961                 this.hidePopup();
22962                 e.preventDefault();
22963                 break;
22964             case 9: // tab
22965                 this.setValue(this.formatDate(this.date));
22966                 this.hidePopup();
22967                 break;
22968             case 16: // shift
22969             case 17: // ctrl
22970             case 18: // alt
22971                 break;
22972             default :
22973                 this.hidePopup();
22974                 
22975         }
22976     },
22977     
22978     
22979     onClick: function(e) 
22980     {
22981         e.stopPropagation();
22982         e.preventDefault();
22983         
22984         var target = e.getTarget();
22985         
22986         if(target.nodeName.toLowerCase() === 'i'){
22987             target = Roo.get(target).dom.parentNode;
22988         }
22989         
22990         var nodeName = target.nodeName;
22991         var className = target.className;
22992         var html = target.innerHTML;
22993         //Roo.log(nodeName);
22994         
22995         switch(nodeName.toLowerCase()) {
22996             case 'th':
22997                 switch(className) {
22998                     case 'switch':
22999                         this.showMode(1);
23000                         break;
23001                     case 'prev':
23002                     case 'next':
23003                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23004                         switch(this.viewMode){
23005                                 case 0:
23006                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23007                                         break;
23008                                 case 1:
23009                                 case 2:
23010                                         this.viewDate = this.moveYear(this.viewDate, dir);
23011                                         break;
23012                         }
23013                         this.fill();
23014                         break;
23015                     case 'today':
23016                         var date = new Date();
23017                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23018 //                        this.fill()
23019                         this.setValue(this.formatDate(this.date));
23020                         
23021                         this.hidePopup();
23022                         break;
23023                 }
23024                 break;
23025             case 'span':
23026                 if (className.indexOf('disabled') < 0) {
23027                 if (!this.viewDate) {
23028                     this.viewDate = new Date();
23029                 }
23030                 this.viewDate.setUTCDate(1);
23031                     if (className.indexOf('month') > -1) {
23032                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23033                     } else {
23034                         var year = parseInt(html, 10) || 0;
23035                         this.viewDate.setUTCFullYear(year);
23036                         
23037                     }
23038                     
23039                     if(this.singleMode){
23040                         this.setValue(this.formatDate(this.viewDate));
23041                         this.hidePopup();
23042                         return;
23043                     }
23044                     
23045                     this.showMode(-1);
23046                     this.fill();
23047                 }
23048                 break;
23049                 
23050             case 'td':
23051                 //Roo.log(className);
23052                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23053                     var day = parseInt(html, 10) || 1;
23054                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23055                         month = (this.viewDate || new Date()).getUTCMonth();
23056
23057                     if (className.indexOf('old') > -1) {
23058                         if(month === 0 ){
23059                             month = 11;
23060                             year -= 1;
23061                         }else{
23062                             month -= 1;
23063                         }
23064                     } else if (className.indexOf('new') > -1) {
23065                         if (month == 11) {
23066                             month = 0;
23067                             year += 1;
23068                         } else {
23069                             month += 1;
23070                         }
23071                     }
23072                     //Roo.log([year,month,day]);
23073                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23074                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23075 //                    this.fill();
23076                     //Roo.log(this.formatDate(this.date));
23077                     this.setValue(this.formatDate(this.date));
23078                     this.hidePopup();
23079                 }
23080                 break;
23081         }
23082     },
23083     
23084     setStartDate: function(startDate)
23085     {
23086         this.startDate = startDate || -Infinity;
23087         if (this.startDate !== -Infinity) {
23088             this.startDate = this.parseDate(this.startDate);
23089         }
23090         this.update();
23091         this.updateNavArrows();
23092     },
23093
23094     setEndDate: function(endDate)
23095     {
23096         this.endDate = endDate || Infinity;
23097         if (this.endDate !== Infinity) {
23098             this.endDate = this.parseDate(this.endDate);
23099         }
23100         this.update();
23101         this.updateNavArrows();
23102     },
23103     
23104     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23105     {
23106         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23107         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23108             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23109         }
23110         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23111             return parseInt(d, 10);
23112         });
23113         this.update();
23114         this.updateNavArrows();
23115     },
23116     
23117     updateNavArrows: function() 
23118     {
23119         if(this.singleMode){
23120             return;
23121         }
23122         
23123         var d = new Date(this.viewDate),
23124         year = d.getUTCFullYear(),
23125         month = d.getUTCMonth();
23126         
23127         Roo.each(this.picker().select('.prev', true).elements, function(v){
23128             v.show();
23129             switch (this.viewMode) {
23130                 case 0:
23131
23132                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23133                         v.hide();
23134                     }
23135                     break;
23136                 case 1:
23137                 case 2:
23138                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23139                         v.hide();
23140                     }
23141                     break;
23142             }
23143         });
23144         
23145         Roo.each(this.picker().select('.next', true).elements, function(v){
23146             v.show();
23147             switch (this.viewMode) {
23148                 case 0:
23149
23150                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23151                         v.hide();
23152                     }
23153                     break;
23154                 case 1:
23155                 case 2:
23156                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23157                         v.hide();
23158                     }
23159                     break;
23160             }
23161         })
23162     },
23163     
23164     moveMonth: function(date, dir)
23165     {
23166         if (!dir) {
23167             return date;
23168         }
23169         var new_date = new Date(date.valueOf()),
23170         day = new_date.getUTCDate(),
23171         month = new_date.getUTCMonth(),
23172         mag = Math.abs(dir),
23173         new_month, test;
23174         dir = dir > 0 ? 1 : -1;
23175         if (mag == 1){
23176             test = dir == -1
23177             // If going back one month, make sure month is not current month
23178             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23179             ? function(){
23180                 return new_date.getUTCMonth() == month;
23181             }
23182             // If going forward one month, make sure month is as expected
23183             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23184             : function(){
23185                 return new_date.getUTCMonth() != new_month;
23186             };
23187             new_month = month + dir;
23188             new_date.setUTCMonth(new_month);
23189             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23190             if (new_month < 0 || new_month > 11) {
23191                 new_month = (new_month + 12) % 12;
23192             }
23193         } else {
23194             // For magnitudes >1, move one month at a time...
23195             for (var i=0; i<mag; i++) {
23196                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23197                 new_date = this.moveMonth(new_date, dir);
23198             }
23199             // ...then reset the day, keeping it in the new month
23200             new_month = new_date.getUTCMonth();
23201             new_date.setUTCDate(day);
23202             test = function(){
23203                 return new_month != new_date.getUTCMonth();
23204             };
23205         }
23206         // Common date-resetting loop -- if date is beyond end of month, make it
23207         // end of month
23208         while (test()){
23209             new_date.setUTCDate(--day);
23210             new_date.setUTCMonth(new_month);
23211         }
23212         return new_date;
23213     },
23214
23215     moveYear: function(date, dir)
23216     {
23217         return this.moveMonth(date, dir*12);
23218     },
23219
23220     dateWithinRange: function(date)
23221     {
23222         return date >= this.startDate && date <= this.endDate;
23223     },
23224
23225     
23226     remove: function() 
23227     {
23228         this.picker().remove();
23229     },
23230     
23231     validateValue : function(value)
23232     {
23233         if(this.getVisibilityEl().hasClass('hidden')){
23234             return true;
23235         }
23236         
23237         if(value.length < 1)  {
23238             if(this.allowBlank){
23239                 return true;
23240             }
23241             return false;
23242         }
23243         
23244         if(value.length < this.minLength){
23245             return false;
23246         }
23247         if(value.length > this.maxLength){
23248             return false;
23249         }
23250         if(this.vtype){
23251             var vt = Roo.form.VTypes;
23252             if(!vt[this.vtype](value, this)){
23253                 return false;
23254             }
23255         }
23256         if(typeof this.validator == "function"){
23257             var msg = this.validator(value);
23258             if(msg !== true){
23259                 return false;
23260             }
23261         }
23262         
23263         if(this.regex && !this.regex.test(value)){
23264             return false;
23265         }
23266         
23267         if(typeof(this.parseDate(value)) == 'undefined'){
23268             return false;
23269         }
23270         
23271         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23272             return false;
23273         }      
23274         
23275         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23276             return false;
23277         } 
23278         
23279         
23280         return true;
23281     },
23282     
23283     reset : function()
23284     {
23285         this.date = this.viewDate = '';
23286         
23287         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23288     }
23289    
23290 });
23291
23292 Roo.apply(Roo.bootstrap.DateField,  {
23293     
23294     head : {
23295         tag: 'thead',
23296         cn: [
23297         {
23298             tag: 'tr',
23299             cn: [
23300             {
23301                 tag: 'th',
23302                 cls: 'prev',
23303                 html: '<i class="fa fa-arrow-left"/>'
23304             },
23305             {
23306                 tag: 'th',
23307                 cls: 'switch',
23308                 colspan: '5'
23309             },
23310             {
23311                 tag: 'th',
23312                 cls: 'next',
23313                 html: '<i class="fa fa-arrow-right"/>'
23314             }
23315
23316             ]
23317         }
23318         ]
23319     },
23320     
23321     content : {
23322         tag: 'tbody',
23323         cn: [
23324         {
23325             tag: 'tr',
23326             cn: [
23327             {
23328                 tag: 'td',
23329                 colspan: '7'
23330             }
23331             ]
23332         }
23333         ]
23334     },
23335     
23336     footer : {
23337         tag: 'tfoot',
23338         cn: [
23339         {
23340             tag: 'tr',
23341             cn: [
23342             {
23343                 tag: 'th',
23344                 colspan: '7',
23345                 cls: 'today'
23346             }
23347                     
23348             ]
23349         }
23350         ]
23351     },
23352     
23353     dates:{
23354         en: {
23355             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23356             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23357             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23358             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23359             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23360             today: "Today"
23361         }
23362     },
23363     
23364     modes: [
23365     {
23366         clsName: 'days',
23367         navFnc: 'Month',
23368         navStep: 1
23369     },
23370     {
23371         clsName: 'months',
23372         navFnc: 'FullYear',
23373         navStep: 1
23374     },
23375     {
23376         clsName: 'years',
23377         navFnc: 'FullYear',
23378         navStep: 10
23379     }]
23380 });
23381
23382 Roo.apply(Roo.bootstrap.DateField,  {
23383   
23384     template : {
23385         tag: 'div',
23386         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23387         cn: [
23388         {
23389             tag: 'div',
23390             cls: 'datepicker-days',
23391             cn: [
23392             {
23393                 tag: 'table',
23394                 cls: 'table-condensed',
23395                 cn:[
23396                 Roo.bootstrap.DateField.head,
23397                 {
23398                     tag: 'tbody'
23399                 },
23400                 Roo.bootstrap.DateField.footer
23401                 ]
23402             }
23403             ]
23404         },
23405         {
23406             tag: 'div',
23407             cls: 'datepicker-months',
23408             cn: [
23409             {
23410                 tag: 'table',
23411                 cls: 'table-condensed',
23412                 cn:[
23413                 Roo.bootstrap.DateField.head,
23414                 Roo.bootstrap.DateField.content,
23415                 Roo.bootstrap.DateField.footer
23416                 ]
23417             }
23418             ]
23419         },
23420         {
23421             tag: 'div',
23422             cls: 'datepicker-years',
23423             cn: [
23424             {
23425                 tag: 'table',
23426                 cls: 'table-condensed',
23427                 cn:[
23428                 Roo.bootstrap.DateField.head,
23429                 Roo.bootstrap.DateField.content,
23430                 Roo.bootstrap.DateField.footer
23431                 ]
23432             }
23433             ]
23434         }
23435         ]
23436     }
23437 });
23438
23439  
23440
23441  /*
23442  * - LGPL
23443  *
23444  * TimeField
23445  * 
23446  */
23447
23448 /**
23449  * @class Roo.bootstrap.TimeField
23450  * @extends Roo.bootstrap.Input
23451  * Bootstrap DateField class
23452  * 
23453  * 
23454  * @constructor
23455  * Create a new TimeField
23456  * @param {Object} config The config object
23457  */
23458
23459 Roo.bootstrap.TimeField = function(config){
23460     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23461     this.addEvents({
23462             /**
23463              * @event show
23464              * Fires when this field show.
23465              * @param {Roo.bootstrap.DateField} thisthis
23466              * @param {Mixed} date The date value
23467              */
23468             show : true,
23469             /**
23470              * @event show
23471              * Fires when this field hide.
23472              * @param {Roo.bootstrap.DateField} this
23473              * @param {Mixed} date The date value
23474              */
23475             hide : true,
23476             /**
23477              * @event select
23478              * Fires when select a date.
23479              * @param {Roo.bootstrap.DateField} this
23480              * @param {Mixed} date The date value
23481              */
23482             select : true
23483         });
23484 };
23485
23486 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23487     
23488     /**
23489      * @cfg {String} format
23490      * The default time format string which can be overriden for localization support.  The format must be
23491      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23492      */
23493     format : "H:i",
23494
23495     getAutoCreate : function()
23496     {
23497         this.after = '<i class="fa far fa-clock"></i>';
23498         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23499         
23500          
23501     },
23502     onRender: function(ct, position)
23503     {
23504         
23505         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23506                 
23507         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23508         
23509         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23510         
23511         this.pop = this.picker().select('>.datepicker-time',true).first();
23512         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23513         
23514         this.picker().on('mousedown', this.onMousedown, this);
23515         this.picker().on('click', this.onClick, this);
23516         
23517         this.picker().addClass('datepicker-dropdown');
23518     
23519         this.fillTime();
23520         this.update();
23521             
23522         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23523         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23524         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23525         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23526         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23527         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23528
23529     },
23530     
23531     fireKey: function(e){
23532         if (!this.picker().isVisible()){
23533             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23534                 this.show();
23535             }
23536             return;
23537         }
23538
23539         e.preventDefault();
23540         
23541         switch(e.keyCode){
23542             case 27: // escape
23543                 this.hide();
23544                 break;
23545             case 37: // left
23546             case 39: // right
23547                 this.onTogglePeriod();
23548                 break;
23549             case 38: // up
23550                 this.onIncrementMinutes();
23551                 break;
23552             case 40: // down
23553                 this.onDecrementMinutes();
23554                 break;
23555             case 13: // enter
23556             case 9: // tab
23557                 this.setTime();
23558                 break;
23559         }
23560     },
23561     
23562     onClick: function(e) {
23563         e.stopPropagation();
23564         e.preventDefault();
23565     },
23566     
23567     picker : function()
23568     {
23569         return this.pickerEl;
23570     },
23571     
23572     fillTime: function()
23573     {    
23574         var time = this.pop.select('tbody', true).first();
23575         
23576         time.dom.innerHTML = '';
23577         
23578         time.createChild({
23579             tag: 'tr',
23580             cn: [
23581                 {
23582                     tag: 'td',
23583                     cn: [
23584                         {
23585                             tag: 'a',
23586                             href: '#',
23587                             cls: 'btn',
23588                             cn: [
23589                                 {
23590                                     tag: 'i',
23591                                     cls: 'hours-up fa fas fa-chevron-up'
23592                                 }
23593                             ]
23594                         } 
23595                     ]
23596                 },
23597                 {
23598                     tag: 'td',
23599                     cls: 'separator'
23600                 },
23601                 {
23602                     tag: 'td',
23603                     cn: [
23604                         {
23605                             tag: 'a',
23606                             href: '#',
23607                             cls: 'btn',
23608                             cn: [
23609                                 {
23610                                     tag: 'i',
23611                                     cls: 'minutes-up fa fas fa-chevron-up'
23612                                 }
23613                             ]
23614                         }
23615                     ]
23616                 },
23617                 {
23618                     tag: 'td',
23619                     cls: 'separator'
23620                 }
23621             ]
23622         });
23623         
23624         time.createChild({
23625             tag: 'tr',
23626             cn: [
23627                 {
23628                     tag: 'td',
23629                     cn: [
23630                         {
23631                             tag: 'span',
23632                             cls: 'timepicker-hour',
23633                             html: '00'
23634                         }  
23635                     ]
23636                 },
23637                 {
23638                     tag: 'td',
23639                     cls: 'separator',
23640                     html: ':'
23641                 },
23642                 {
23643                     tag: 'td',
23644                     cn: [
23645                         {
23646                             tag: 'span',
23647                             cls: 'timepicker-minute',
23648                             html: '00'
23649                         }  
23650                     ]
23651                 },
23652                 {
23653                     tag: 'td',
23654                     cls: 'separator'
23655                 },
23656                 {
23657                     tag: 'td',
23658                     cn: [
23659                         {
23660                             tag: 'button',
23661                             type: 'button',
23662                             cls: 'btn btn-primary period',
23663                             html: 'AM'
23664                             
23665                         }
23666                     ]
23667                 }
23668             ]
23669         });
23670         
23671         time.createChild({
23672             tag: 'tr',
23673             cn: [
23674                 {
23675                     tag: 'td',
23676                     cn: [
23677                         {
23678                             tag: 'a',
23679                             href: '#',
23680                             cls: 'btn',
23681                             cn: [
23682                                 {
23683                                     tag: 'span',
23684                                     cls: 'hours-down fa fas fa-chevron-down'
23685                                 }
23686                             ]
23687                         }
23688                     ]
23689                 },
23690                 {
23691                     tag: 'td',
23692                     cls: 'separator'
23693                 },
23694                 {
23695                     tag: 'td',
23696                     cn: [
23697                         {
23698                             tag: 'a',
23699                             href: '#',
23700                             cls: 'btn',
23701                             cn: [
23702                                 {
23703                                     tag: 'span',
23704                                     cls: 'minutes-down fa fas fa-chevron-down'
23705                                 }
23706                             ]
23707                         }
23708                     ]
23709                 },
23710                 {
23711                     tag: 'td',
23712                     cls: 'separator'
23713                 }
23714             ]
23715         });
23716         
23717     },
23718     
23719     update: function()
23720     {
23721         
23722         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23723         
23724         this.fill();
23725     },
23726     
23727     fill: function() 
23728     {
23729         var hours = this.time.getHours();
23730         var minutes = this.time.getMinutes();
23731         var period = 'AM';
23732         
23733         if(hours > 11){
23734             period = 'PM';
23735         }
23736         
23737         if(hours == 0){
23738             hours = 12;
23739         }
23740         
23741         
23742         if(hours > 12){
23743             hours = hours - 12;
23744         }
23745         
23746         if(hours < 10){
23747             hours = '0' + hours;
23748         }
23749         
23750         if(minutes < 10){
23751             minutes = '0' + minutes;
23752         }
23753         
23754         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23755         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23756         this.pop.select('button', true).first().dom.innerHTML = period;
23757         
23758     },
23759     
23760     place: function()
23761     {   
23762         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23763         
23764         var cls = ['bottom'];
23765         
23766         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23767             cls.pop();
23768             cls.push('top');
23769         }
23770         
23771         cls.push('right');
23772         
23773         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23774             cls.pop();
23775             cls.push('left');
23776         }
23777         //this.picker().setXY(20000,20000);
23778         this.picker().addClass(cls.join('-'));
23779         
23780         var _this = this;
23781         
23782         Roo.each(cls, function(c){
23783             if(c == 'bottom'){
23784                 (function() {
23785                  //  
23786                 }).defer(200);
23787                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23788                 //_this.picker().setTop(_this.inputEl().getHeight());
23789                 return;
23790             }
23791             if(c == 'top'){
23792                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23793                 
23794                 //_this.picker().setTop(0 - _this.picker().getHeight());
23795                 return;
23796             }
23797             /*
23798             if(c == 'left'){
23799                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23800                 return;
23801             }
23802             if(c == 'right'){
23803                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23804                 return;
23805             }
23806             */
23807         });
23808         
23809     },
23810   
23811     onFocus : function()
23812     {
23813         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23814         this.show();
23815     },
23816     
23817     onBlur : function()
23818     {
23819         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23820         this.hide();
23821     },
23822     
23823     show : function()
23824     {
23825         this.picker().show();
23826         this.pop.show();
23827         this.update();
23828         this.place();
23829         
23830         this.fireEvent('show', this, this.date);
23831     },
23832     
23833     hide : function()
23834     {
23835         this.picker().hide();
23836         this.pop.hide();
23837         
23838         this.fireEvent('hide', this, this.date);
23839     },
23840     
23841     setTime : function()
23842     {
23843         this.hide();
23844         this.setValue(this.time.format(this.format));
23845         
23846         this.fireEvent('select', this, this.date);
23847         
23848         
23849     },
23850     
23851     onMousedown: function(e){
23852         e.stopPropagation();
23853         e.preventDefault();
23854     },
23855     
23856     onIncrementHours: function()
23857     {
23858         Roo.log('onIncrementHours');
23859         this.time = this.time.add(Date.HOUR, 1);
23860         this.update();
23861         
23862     },
23863     
23864     onDecrementHours: function()
23865     {
23866         Roo.log('onDecrementHours');
23867         this.time = this.time.add(Date.HOUR, -1);
23868         this.update();
23869     },
23870     
23871     onIncrementMinutes: function()
23872     {
23873         Roo.log('onIncrementMinutes');
23874         this.time = this.time.add(Date.MINUTE, 1);
23875         this.update();
23876     },
23877     
23878     onDecrementMinutes: function()
23879     {
23880         Roo.log('onDecrementMinutes');
23881         this.time = this.time.add(Date.MINUTE, -1);
23882         this.update();
23883     },
23884     
23885     onTogglePeriod: function()
23886     {
23887         Roo.log('onTogglePeriod');
23888         this.time = this.time.add(Date.HOUR, 12);
23889         this.update();
23890     }
23891     
23892    
23893 });
23894  
23895
23896 Roo.apply(Roo.bootstrap.TimeField,  {
23897   
23898     template : {
23899         tag: 'div',
23900         cls: 'datepicker dropdown-menu',
23901         cn: [
23902             {
23903                 tag: 'div',
23904                 cls: 'datepicker-time',
23905                 cn: [
23906                 {
23907                     tag: 'table',
23908                     cls: 'table-condensed',
23909                     cn:[
23910                         {
23911                             tag: 'tbody',
23912                             cn: [
23913                                 {
23914                                     tag: 'tr',
23915                                     cn: [
23916                                     {
23917                                         tag: 'td',
23918                                         colspan: '7'
23919                                     }
23920                                     ]
23921                                 }
23922                             ]
23923                         },
23924                         {
23925                             tag: 'tfoot',
23926                             cn: [
23927                                 {
23928                                     tag: 'tr',
23929                                     cn: [
23930                                     {
23931                                         tag: 'th',
23932                                         colspan: '7',
23933                                         cls: '',
23934                                         cn: [
23935                                             {
23936                                                 tag: 'button',
23937                                                 cls: 'btn btn-info ok',
23938                                                 html: 'OK'
23939                                             }
23940                                         ]
23941                                     }
23942                     
23943                                     ]
23944                                 }
23945                             ]
23946                         }
23947                     ]
23948                 }
23949                 ]
23950             }
23951         ]
23952     }
23953 });
23954
23955  
23956
23957  /*
23958  * - LGPL
23959  *
23960  * MonthField
23961  * 
23962  */
23963
23964 /**
23965  * @class Roo.bootstrap.MonthField
23966  * @extends Roo.bootstrap.Input
23967  * Bootstrap MonthField class
23968  * 
23969  * @cfg {String} language default en
23970  * 
23971  * @constructor
23972  * Create a new MonthField
23973  * @param {Object} config The config object
23974  */
23975
23976 Roo.bootstrap.MonthField = function(config){
23977     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23978     
23979     this.addEvents({
23980         /**
23981          * @event show
23982          * Fires when this field show.
23983          * @param {Roo.bootstrap.MonthField} this
23984          * @param {Mixed} date The date value
23985          */
23986         show : true,
23987         /**
23988          * @event show
23989          * Fires when this field hide.
23990          * @param {Roo.bootstrap.MonthField} this
23991          * @param {Mixed} date The date value
23992          */
23993         hide : true,
23994         /**
23995          * @event select
23996          * Fires when select a date.
23997          * @param {Roo.bootstrap.MonthField} this
23998          * @param {String} oldvalue The old value
23999          * @param {String} newvalue The new value
24000          */
24001         select : true
24002     });
24003 };
24004
24005 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24006     
24007     onRender: function(ct, position)
24008     {
24009         
24010         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24011         
24012         this.language = this.language || 'en';
24013         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24014         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24015         
24016         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24017         this.isInline = false;
24018         this.isInput = true;
24019         this.component = this.el.select('.add-on', true).first() || false;
24020         this.component = (this.component && this.component.length === 0) ? false : this.component;
24021         this.hasInput = this.component && this.inputEL().length;
24022         
24023         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24024         
24025         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24026         
24027         this.picker().on('mousedown', this.onMousedown, this);
24028         this.picker().on('click', this.onClick, this);
24029         
24030         this.picker().addClass('datepicker-dropdown');
24031         
24032         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24033             v.setStyle('width', '189px');
24034         });
24035         
24036         this.fillMonths();
24037         
24038         this.update();
24039         
24040         if(this.isInline) {
24041             this.show();
24042         }
24043         
24044     },
24045     
24046     setValue: function(v, suppressEvent)
24047     {   
24048         var o = this.getValue();
24049         
24050         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24051         
24052         this.update();
24053
24054         if(suppressEvent !== true){
24055             this.fireEvent('select', this, o, v);
24056         }
24057         
24058     },
24059     
24060     getValue: function()
24061     {
24062         return this.value;
24063     },
24064     
24065     onClick: function(e) 
24066     {
24067         e.stopPropagation();
24068         e.preventDefault();
24069         
24070         var target = e.getTarget();
24071         
24072         if(target.nodeName.toLowerCase() === 'i'){
24073             target = Roo.get(target).dom.parentNode;
24074         }
24075         
24076         var nodeName = target.nodeName;
24077         var className = target.className;
24078         var html = target.innerHTML;
24079         
24080         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24081             return;
24082         }
24083         
24084         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24085         
24086         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24087         
24088         this.hide();
24089                         
24090     },
24091     
24092     picker : function()
24093     {
24094         return this.pickerEl;
24095     },
24096     
24097     fillMonths: function()
24098     {    
24099         var i = 0;
24100         var months = this.picker().select('>.datepicker-months td', true).first();
24101         
24102         months.dom.innerHTML = '';
24103         
24104         while (i < 12) {
24105             var month = {
24106                 tag: 'span',
24107                 cls: 'month',
24108                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24109             };
24110             
24111             months.createChild(month);
24112         }
24113         
24114     },
24115     
24116     update: function()
24117     {
24118         var _this = this;
24119         
24120         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24121             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24122         }
24123         
24124         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24125             e.removeClass('active');
24126             
24127             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24128                 e.addClass('active');
24129             }
24130         })
24131     },
24132     
24133     place: function()
24134     {
24135         if(this.isInline) {
24136             return;
24137         }
24138         
24139         this.picker().removeClass(['bottom', 'top']);
24140         
24141         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24142             /*
24143              * place to the top of element!
24144              *
24145              */
24146             
24147             this.picker().addClass('top');
24148             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24149             
24150             return;
24151         }
24152         
24153         this.picker().addClass('bottom');
24154         
24155         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24156     },
24157     
24158     onFocus : function()
24159     {
24160         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24161         this.show();
24162     },
24163     
24164     onBlur : function()
24165     {
24166         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24167         
24168         var d = this.inputEl().getValue();
24169         
24170         this.setValue(d);
24171                 
24172         this.hide();
24173     },
24174     
24175     show : function()
24176     {
24177         this.picker().show();
24178         this.picker().select('>.datepicker-months', true).first().show();
24179         this.update();
24180         this.place();
24181         
24182         this.fireEvent('show', this, this.date);
24183     },
24184     
24185     hide : function()
24186     {
24187         if(this.isInline) {
24188             return;
24189         }
24190         this.picker().hide();
24191         this.fireEvent('hide', this, this.date);
24192         
24193     },
24194     
24195     onMousedown: function(e)
24196     {
24197         e.stopPropagation();
24198         e.preventDefault();
24199     },
24200     
24201     keyup: function(e)
24202     {
24203         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24204         this.update();
24205     },
24206
24207     fireKey: function(e)
24208     {
24209         if (!this.picker().isVisible()){
24210             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24211                 this.show();
24212             }
24213             return;
24214         }
24215         
24216         var dir;
24217         
24218         switch(e.keyCode){
24219             case 27: // escape
24220                 this.hide();
24221                 e.preventDefault();
24222                 break;
24223             case 37: // left
24224             case 39: // right
24225                 dir = e.keyCode == 37 ? -1 : 1;
24226                 
24227                 this.vIndex = this.vIndex + dir;
24228                 
24229                 if(this.vIndex < 0){
24230                     this.vIndex = 0;
24231                 }
24232                 
24233                 if(this.vIndex > 11){
24234                     this.vIndex = 11;
24235                 }
24236                 
24237                 if(isNaN(this.vIndex)){
24238                     this.vIndex = 0;
24239                 }
24240                 
24241                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24242                 
24243                 break;
24244             case 38: // up
24245             case 40: // down
24246                 
24247                 dir = e.keyCode == 38 ? -1 : 1;
24248                 
24249                 this.vIndex = this.vIndex + dir * 4;
24250                 
24251                 if(this.vIndex < 0){
24252                     this.vIndex = 0;
24253                 }
24254                 
24255                 if(this.vIndex > 11){
24256                     this.vIndex = 11;
24257                 }
24258                 
24259                 if(isNaN(this.vIndex)){
24260                     this.vIndex = 0;
24261                 }
24262                 
24263                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24264                 break;
24265                 
24266             case 13: // enter
24267                 
24268                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24269                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24270                 }
24271                 
24272                 this.hide();
24273                 e.preventDefault();
24274                 break;
24275             case 9: // tab
24276                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24277                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24278                 }
24279                 this.hide();
24280                 break;
24281             case 16: // shift
24282             case 17: // ctrl
24283             case 18: // alt
24284                 break;
24285             default :
24286                 this.hide();
24287                 
24288         }
24289     },
24290     
24291     remove: function() 
24292     {
24293         this.picker().remove();
24294     }
24295    
24296 });
24297
24298 Roo.apply(Roo.bootstrap.MonthField,  {
24299     
24300     content : {
24301         tag: 'tbody',
24302         cn: [
24303         {
24304             tag: 'tr',
24305             cn: [
24306             {
24307                 tag: 'td',
24308                 colspan: '7'
24309             }
24310             ]
24311         }
24312         ]
24313     },
24314     
24315     dates:{
24316         en: {
24317             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24318             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24319         }
24320     }
24321 });
24322
24323 Roo.apply(Roo.bootstrap.MonthField,  {
24324   
24325     template : {
24326         tag: 'div',
24327         cls: 'datepicker dropdown-menu roo-dynamic',
24328         cn: [
24329             {
24330                 tag: 'div',
24331                 cls: 'datepicker-months',
24332                 cn: [
24333                 {
24334                     tag: 'table',
24335                     cls: 'table-condensed',
24336                     cn:[
24337                         Roo.bootstrap.DateField.content
24338                     ]
24339                 }
24340                 ]
24341             }
24342         ]
24343     }
24344 });
24345
24346  
24347
24348  
24349  /*
24350  * - LGPL
24351  *
24352  * CheckBox
24353  * 
24354  */
24355
24356 /**
24357  * @class Roo.bootstrap.CheckBox
24358  * @extends Roo.bootstrap.Input
24359  * Bootstrap CheckBox class
24360  * 
24361  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24362  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24363  * @cfg {String} boxLabel The text that appears beside the checkbox
24364  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24365  * @cfg {Boolean} checked initnal the element
24366  * @cfg {Boolean} inline inline the element (default false)
24367  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24368  * @cfg {String} tooltip label tooltip
24369  * 
24370  * @constructor
24371  * Create a new CheckBox
24372  * @param {Object} config The config object
24373  */
24374
24375 Roo.bootstrap.CheckBox = function(config){
24376     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24377    
24378     this.addEvents({
24379         /**
24380         * @event check
24381         * Fires when the element is checked or unchecked.
24382         * @param {Roo.bootstrap.CheckBox} this This input
24383         * @param {Boolean} checked The new checked value
24384         */
24385        check : true,
24386        /**
24387         * @event click
24388         * Fires when the element is click.
24389         * @param {Roo.bootstrap.CheckBox} this This input
24390         */
24391        click : true
24392     });
24393     
24394 };
24395
24396 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24397   
24398     inputType: 'checkbox',
24399     inputValue: 1,
24400     valueOff: 0,
24401     boxLabel: false,
24402     checked: false,
24403     weight : false,
24404     inline: false,
24405     tooltip : '',
24406     
24407     // checkbox success does not make any sense really.. 
24408     invalidClass : "",
24409     validClass : "",
24410     
24411     
24412     getAutoCreate : function()
24413     {
24414         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24415         
24416         var id = Roo.id();
24417         
24418         var cfg = {};
24419         
24420         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24421         
24422         if(this.inline){
24423             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24424         }
24425         
24426         var input =  {
24427             tag: 'input',
24428             id : id,
24429             type : this.inputType,
24430             value : this.inputValue,
24431             cls : 'roo-' + this.inputType, //'form-box',
24432             placeholder : this.placeholder || ''
24433             
24434         };
24435         
24436         if(this.inputType != 'radio'){
24437             var hidden =  {
24438                 tag: 'input',
24439                 type : 'hidden',
24440                 cls : 'roo-hidden-value',
24441                 value : this.checked ? this.inputValue : this.valueOff
24442             };
24443         }
24444         
24445             
24446         if (this.weight) { // Validity check?
24447             cfg.cls += " " + this.inputType + "-" + this.weight;
24448         }
24449         
24450         if (this.disabled) {
24451             input.disabled=true;
24452         }
24453         
24454         if(this.checked){
24455             input.checked = this.checked;
24456         }
24457         
24458         if (this.name) {
24459             
24460             input.name = this.name;
24461             
24462             if(this.inputType != 'radio'){
24463                 hidden.name = this.name;
24464                 input.name = '_hidden_' + this.name;
24465             }
24466         }
24467         
24468         if (this.size) {
24469             input.cls += ' input-' + this.size;
24470         }
24471         
24472         var settings=this;
24473         
24474         ['xs','sm','md','lg'].map(function(size){
24475             if (settings[size]) {
24476                 cfg.cls += ' col-' + size + '-' + settings[size];
24477             }
24478         });
24479         
24480         var inputblock = input;
24481          
24482         if (this.before || this.after) {
24483             
24484             inputblock = {
24485                 cls : 'input-group',
24486                 cn :  [] 
24487             };
24488             
24489             if (this.before) {
24490                 inputblock.cn.push({
24491                     tag :'span',
24492                     cls : 'input-group-addon',
24493                     html : this.before
24494                 });
24495             }
24496             
24497             inputblock.cn.push(input);
24498             
24499             if(this.inputType != 'radio'){
24500                 inputblock.cn.push(hidden);
24501             }
24502             
24503             if (this.after) {
24504                 inputblock.cn.push({
24505                     tag :'span',
24506                     cls : 'input-group-addon',
24507                     html : this.after
24508                 });
24509             }
24510             
24511         }
24512         var boxLabelCfg = false;
24513         
24514         if(this.boxLabel){
24515            
24516             boxLabelCfg = {
24517                 tag: 'label',
24518                 //'for': id, // box label is handled by onclick - so no for...
24519                 cls: 'box-label',
24520                 html: this.boxLabel
24521             };
24522             if(this.tooltip){
24523                 boxLabelCfg.tooltip = this.tooltip;
24524             }
24525              
24526         }
24527         
24528         
24529         if (align ==='left' && this.fieldLabel.length) {
24530 //                Roo.log("left and has label");
24531             cfg.cn = [
24532                 {
24533                     tag: 'label',
24534                     'for' :  id,
24535                     cls : 'control-label',
24536                     html : this.fieldLabel
24537                 },
24538                 {
24539                     cls : "", 
24540                     cn: [
24541                         inputblock
24542                     ]
24543                 }
24544             ];
24545             
24546             if (boxLabelCfg) {
24547                 cfg.cn[1].cn.push(boxLabelCfg);
24548             }
24549             
24550             if(this.labelWidth > 12){
24551                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24552             }
24553             
24554             if(this.labelWidth < 13 && this.labelmd == 0){
24555                 this.labelmd = this.labelWidth;
24556             }
24557             
24558             if(this.labellg > 0){
24559                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24560                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24561             }
24562             
24563             if(this.labelmd > 0){
24564                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24565                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24566             }
24567             
24568             if(this.labelsm > 0){
24569                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24570                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24571             }
24572             
24573             if(this.labelxs > 0){
24574                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24575                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24576             }
24577             
24578         } else if ( this.fieldLabel.length) {
24579 //                Roo.log(" label");
24580                 cfg.cn = [
24581                    
24582                     {
24583                         tag: this.boxLabel ? 'span' : 'label',
24584                         'for': id,
24585                         cls: 'control-label box-input-label',
24586                         //cls : 'input-group-addon',
24587                         html : this.fieldLabel
24588                     },
24589                     
24590                     inputblock
24591                     
24592                 ];
24593                 if (boxLabelCfg) {
24594                     cfg.cn.push(boxLabelCfg);
24595                 }
24596
24597         } else {
24598             
24599 //                Roo.log(" no label && no align");
24600                 cfg.cn = [  inputblock ] ;
24601                 if (boxLabelCfg) {
24602                     cfg.cn.push(boxLabelCfg);
24603                 }
24604
24605                 
24606         }
24607         
24608        
24609         
24610         if(this.inputType != 'radio'){
24611             cfg.cn.push(hidden);
24612         }
24613         
24614         return cfg;
24615         
24616     },
24617     
24618     /**
24619      * return the real input element.
24620      */
24621     inputEl: function ()
24622     {
24623         return this.el.select('input.roo-' + this.inputType,true).first();
24624     },
24625     hiddenEl: function ()
24626     {
24627         return this.el.select('input.roo-hidden-value',true).first();
24628     },
24629     
24630     labelEl: function()
24631     {
24632         return this.el.select('label.control-label',true).first();
24633     },
24634     /* depricated... */
24635     
24636     label: function()
24637     {
24638         return this.labelEl();
24639     },
24640     
24641     boxLabelEl: function()
24642     {
24643         return this.el.select('label.box-label',true).first();
24644     },
24645     
24646     initEvents : function()
24647     {
24648 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24649         
24650         this.inputEl().on('click', this.onClick,  this);
24651         
24652         if (this.boxLabel) { 
24653             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24654         }
24655         
24656         this.startValue = this.getValue();
24657         
24658         if(this.groupId){
24659             Roo.bootstrap.CheckBox.register(this);
24660         }
24661     },
24662     
24663     onClick : function(e)
24664     {   
24665         if(this.fireEvent('click', this, e) !== false){
24666             this.setChecked(!this.checked);
24667         }
24668         
24669     },
24670     
24671     setChecked : function(state,suppressEvent)
24672     {
24673         this.startValue = this.getValue();
24674
24675         if(this.inputType == 'radio'){
24676             
24677             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24678                 e.dom.checked = false;
24679             });
24680             
24681             this.inputEl().dom.checked = true;
24682             
24683             this.inputEl().dom.value = this.inputValue;
24684             
24685             if(suppressEvent !== true){
24686                 this.fireEvent('check', this, true);
24687             }
24688             
24689             this.validate();
24690             
24691             return;
24692         }
24693         
24694         this.checked = state;
24695         
24696         this.inputEl().dom.checked = state;
24697         
24698         
24699         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24700         
24701         if(suppressEvent !== true){
24702             this.fireEvent('check', this, state);
24703         }
24704         
24705         this.validate();
24706     },
24707     
24708     getValue : function()
24709     {
24710         if(this.inputType == 'radio'){
24711             return this.getGroupValue();
24712         }
24713         
24714         return this.hiddenEl().dom.value;
24715         
24716     },
24717     
24718     getGroupValue : function()
24719     {
24720         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24721             return '';
24722         }
24723         
24724         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24725     },
24726     
24727     setValue : function(v,suppressEvent)
24728     {
24729         if(this.inputType == 'radio'){
24730             this.setGroupValue(v, suppressEvent);
24731             return;
24732         }
24733         
24734         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24735         
24736         this.validate();
24737     },
24738     
24739     setGroupValue : function(v, suppressEvent)
24740     {
24741         this.startValue = this.getValue();
24742         
24743         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24744             e.dom.checked = false;
24745             
24746             if(e.dom.value == v){
24747                 e.dom.checked = true;
24748             }
24749         });
24750         
24751         if(suppressEvent !== true){
24752             this.fireEvent('check', this, true);
24753         }
24754
24755         this.validate();
24756         
24757         return;
24758     },
24759     
24760     validate : function()
24761     {
24762         if(this.getVisibilityEl().hasClass('hidden')){
24763             return true;
24764         }
24765         
24766         if(
24767                 this.disabled || 
24768                 (this.inputType == 'radio' && this.validateRadio()) ||
24769                 (this.inputType == 'checkbox' && this.validateCheckbox())
24770         ){
24771             this.markValid();
24772             return true;
24773         }
24774         
24775         this.markInvalid();
24776         return false;
24777     },
24778     
24779     validateRadio : function()
24780     {
24781         if(this.getVisibilityEl().hasClass('hidden')){
24782             return true;
24783         }
24784         
24785         if(this.allowBlank){
24786             return true;
24787         }
24788         
24789         var valid = false;
24790         
24791         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24792             if(!e.dom.checked){
24793                 return;
24794             }
24795             
24796             valid = true;
24797             
24798             return false;
24799         });
24800         
24801         return valid;
24802     },
24803     
24804     validateCheckbox : function()
24805     {
24806         if(!this.groupId){
24807             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24808             //return (this.getValue() == this.inputValue) ? true : false;
24809         }
24810         
24811         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24812         
24813         if(!group){
24814             return false;
24815         }
24816         
24817         var r = false;
24818         
24819         for(var i in group){
24820             if(group[i].el.isVisible(true)){
24821                 r = false;
24822                 break;
24823             }
24824             
24825             r = true;
24826         }
24827         
24828         for(var i in group){
24829             if(r){
24830                 break;
24831             }
24832             
24833             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24834         }
24835         
24836         return r;
24837     },
24838     
24839     /**
24840      * Mark this field as valid
24841      */
24842     markValid : function()
24843     {
24844         var _this = this;
24845         
24846         this.fireEvent('valid', this);
24847         
24848         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24849         
24850         if(this.groupId){
24851             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24852         }
24853         
24854         if(label){
24855             label.markValid();
24856         }
24857
24858         if(this.inputType == 'radio'){
24859             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24860                 var fg = e.findParent('.form-group', false, true);
24861                 if (Roo.bootstrap.version == 3) {
24862                     fg.removeClass([_this.invalidClass, _this.validClass]);
24863                     fg.addClass(_this.validClass);
24864                 } else {
24865                     fg.removeClass(['is-valid', 'is-invalid']);
24866                     fg.addClass('is-valid');
24867                 }
24868             });
24869             
24870             return;
24871         }
24872
24873         if(!this.groupId){
24874             var fg = this.el.findParent('.form-group', false, true);
24875             if (Roo.bootstrap.version == 3) {
24876                 fg.removeClass([this.invalidClass, this.validClass]);
24877                 fg.addClass(this.validClass);
24878             } else {
24879                 fg.removeClass(['is-valid', 'is-invalid']);
24880                 fg.addClass('is-valid');
24881             }
24882             return;
24883         }
24884         
24885         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24886         
24887         if(!group){
24888             return;
24889         }
24890         
24891         for(var i in group){
24892             var fg = group[i].el.findParent('.form-group', false, true);
24893             if (Roo.bootstrap.version == 3) {
24894                 fg.removeClass([this.invalidClass, this.validClass]);
24895                 fg.addClass(this.validClass);
24896             } else {
24897                 fg.removeClass(['is-valid', 'is-invalid']);
24898                 fg.addClass('is-valid');
24899             }
24900         }
24901     },
24902     
24903      /**
24904      * Mark this field as invalid
24905      * @param {String} msg The validation message
24906      */
24907     markInvalid : function(msg)
24908     {
24909         if(this.allowBlank){
24910             return;
24911         }
24912         
24913         var _this = this;
24914         
24915         this.fireEvent('invalid', this, msg);
24916         
24917         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24918         
24919         if(this.groupId){
24920             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24921         }
24922         
24923         if(label){
24924             label.markInvalid();
24925         }
24926             
24927         if(this.inputType == 'radio'){
24928             
24929             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24930                 var fg = e.findParent('.form-group', false, true);
24931                 if (Roo.bootstrap.version == 3) {
24932                     fg.removeClass([_this.invalidClass, _this.validClass]);
24933                     fg.addClass(_this.invalidClass);
24934                 } else {
24935                     fg.removeClass(['is-invalid', 'is-valid']);
24936                     fg.addClass('is-invalid');
24937                 }
24938             });
24939             
24940             return;
24941         }
24942         
24943         if(!this.groupId){
24944             var fg = this.el.findParent('.form-group', false, true);
24945             if (Roo.bootstrap.version == 3) {
24946                 fg.removeClass([_this.invalidClass, _this.validClass]);
24947                 fg.addClass(_this.invalidClass);
24948             } else {
24949                 fg.removeClass(['is-invalid', 'is-valid']);
24950                 fg.addClass('is-invalid');
24951             }
24952             return;
24953         }
24954         
24955         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24956         
24957         if(!group){
24958             return;
24959         }
24960         
24961         for(var i in group){
24962             var fg = group[i].el.findParent('.form-group', false, true);
24963             if (Roo.bootstrap.version == 3) {
24964                 fg.removeClass([_this.invalidClass, _this.validClass]);
24965                 fg.addClass(_this.invalidClass);
24966             } else {
24967                 fg.removeClass(['is-invalid', 'is-valid']);
24968                 fg.addClass('is-invalid');
24969             }
24970         }
24971         
24972     },
24973     
24974     clearInvalid : function()
24975     {
24976         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24977         
24978         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24979         
24980         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24981         
24982         if (label && label.iconEl) {
24983             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24984             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24985         }
24986     },
24987     
24988     disable : function()
24989     {
24990         if(this.inputType != 'radio'){
24991             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24992             return;
24993         }
24994         
24995         var _this = this;
24996         
24997         if(this.rendered){
24998             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24999                 _this.getActionEl().addClass(this.disabledClass);
25000                 e.dom.disabled = true;
25001             });
25002         }
25003         
25004         this.disabled = true;
25005         this.fireEvent("disable", this);
25006         return this;
25007     },
25008
25009     enable : function()
25010     {
25011         if(this.inputType != 'radio'){
25012             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25013             return;
25014         }
25015         
25016         var _this = this;
25017         
25018         if(this.rendered){
25019             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25020                 _this.getActionEl().removeClass(this.disabledClass);
25021                 e.dom.disabled = false;
25022             });
25023         }
25024         
25025         this.disabled = false;
25026         this.fireEvent("enable", this);
25027         return this;
25028     },
25029     
25030     setBoxLabel : function(v)
25031     {
25032         this.boxLabel = v;
25033         
25034         if(this.rendered){
25035             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25036         }
25037     }
25038
25039 });
25040
25041 Roo.apply(Roo.bootstrap.CheckBox, {
25042     
25043     groups: {},
25044     
25045      /**
25046     * register a CheckBox Group
25047     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25048     */
25049     register : function(checkbox)
25050     {
25051         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25052             this.groups[checkbox.groupId] = {};
25053         }
25054         
25055         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25056             return;
25057         }
25058         
25059         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25060         
25061     },
25062     /**
25063     * fetch a CheckBox Group based on the group ID
25064     * @param {string} the group ID
25065     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25066     */
25067     get: function(groupId) {
25068         if (typeof(this.groups[groupId]) == 'undefined') {
25069             return false;
25070         }
25071         
25072         return this.groups[groupId] ;
25073     }
25074     
25075     
25076 });
25077 /*
25078  * - LGPL
25079  *
25080  * RadioItem
25081  * 
25082  */
25083
25084 /**
25085  * @class Roo.bootstrap.Radio
25086  * @extends Roo.bootstrap.Component
25087  * Bootstrap Radio class
25088  * @cfg {String} boxLabel - the label associated
25089  * @cfg {String} value - the value of radio
25090  * 
25091  * @constructor
25092  * Create a new Radio
25093  * @param {Object} config The config object
25094  */
25095 Roo.bootstrap.Radio = function(config){
25096     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25097     
25098 };
25099
25100 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25101     
25102     boxLabel : '',
25103     
25104     value : '',
25105     
25106     getAutoCreate : function()
25107     {
25108         var cfg = {
25109             tag : 'div',
25110             cls : 'form-group radio',
25111             cn : [
25112                 {
25113                     tag : 'label',
25114                     cls : 'box-label',
25115                     html : this.boxLabel
25116                 }
25117             ]
25118         };
25119         
25120         return cfg;
25121     },
25122     
25123     initEvents : function() 
25124     {
25125         this.parent().register(this);
25126         
25127         this.el.on('click', this.onClick, this);
25128         
25129     },
25130     
25131     onClick : function(e)
25132     {
25133         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25134             this.setChecked(true);
25135         }
25136     },
25137     
25138     setChecked : function(state, suppressEvent)
25139     {
25140         this.parent().setValue(this.value, suppressEvent);
25141         
25142     },
25143     
25144     setBoxLabel : function(v)
25145     {
25146         this.boxLabel = v;
25147         
25148         if(this.rendered){
25149             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25150         }
25151     }
25152     
25153 });
25154  
25155
25156  /*
25157  * - LGPL
25158  *
25159  * Input
25160  * 
25161  */
25162
25163 /**
25164  * @class Roo.bootstrap.SecurePass
25165  * @extends Roo.bootstrap.Input
25166  * Bootstrap SecurePass class
25167  *
25168  * 
25169  * @constructor
25170  * Create a new SecurePass
25171  * @param {Object} config The config object
25172  */
25173  
25174 Roo.bootstrap.SecurePass = function (config) {
25175     // these go here, so the translation tool can replace them..
25176     this.errors = {
25177         PwdEmpty: "Please type a password, and then retype it to confirm.",
25178         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25179         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25180         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25181         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25182         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25183         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25184         TooWeak: "Your password is Too Weak."
25185     },
25186     this.meterLabel = "Password strength:";
25187     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25188     this.meterClass = [
25189         "roo-password-meter-tooweak", 
25190         "roo-password-meter-weak", 
25191         "roo-password-meter-medium", 
25192         "roo-password-meter-strong", 
25193         "roo-password-meter-grey"
25194     ];
25195     
25196     this.errors = {};
25197     
25198     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25199 }
25200
25201 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25202     /**
25203      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25204      * {
25205      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25206      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25207      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25208      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25209      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25210      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25211      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25212      * })
25213      */
25214     // private
25215     
25216     meterWidth: 300,
25217     errorMsg :'',    
25218     errors: false,
25219     imageRoot: '/',
25220     /**
25221      * @cfg {String/Object} Label for the strength meter (defaults to
25222      * 'Password strength:')
25223      */
25224     // private
25225     meterLabel: '',
25226     /**
25227      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25228      * ['Weak', 'Medium', 'Strong'])
25229      */
25230     // private    
25231     pwdStrengths: false,    
25232     // private
25233     strength: 0,
25234     // private
25235     _lastPwd: null,
25236     // private
25237     kCapitalLetter: 0,
25238     kSmallLetter: 1,
25239     kDigit: 2,
25240     kPunctuation: 3,
25241     
25242     insecure: false,
25243     // private
25244     initEvents: function ()
25245     {
25246         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25247
25248         if (this.el.is('input[type=password]') && Roo.isSafari) {
25249             this.el.on('keydown', this.SafariOnKeyDown, this);
25250         }
25251
25252         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25253     },
25254     // private
25255     onRender: function (ct, position)
25256     {
25257         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25258         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25259         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25260
25261         this.trigger.createChild({
25262                    cn: [
25263                     {
25264                     //id: 'PwdMeter',
25265                     tag: 'div',
25266                     cls: 'roo-password-meter-grey col-xs-12',
25267                     style: {
25268                         //width: 0,
25269                         //width: this.meterWidth + 'px'                                                
25270                         }
25271                     },
25272                     {                            
25273                          cls: 'roo-password-meter-text'                          
25274                     }
25275                 ]            
25276         });
25277
25278          
25279         if (this.hideTrigger) {
25280             this.trigger.setDisplayed(false);
25281         }
25282         this.setSize(this.width || '', this.height || '');
25283     },
25284     // private
25285     onDestroy: function ()
25286     {
25287         if (this.trigger) {
25288             this.trigger.removeAllListeners();
25289             this.trigger.remove();
25290         }
25291         if (this.wrap) {
25292             this.wrap.remove();
25293         }
25294         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25295     },
25296     // private
25297     checkStrength: function ()
25298     {
25299         var pwd = this.inputEl().getValue();
25300         if (pwd == this._lastPwd) {
25301             return;
25302         }
25303
25304         var strength;
25305         if (this.ClientSideStrongPassword(pwd)) {
25306             strength = 3;
25307         } else if (this.ClientSideMediumPassword(pwd)) {
25308             strength = 2;
25309         } else if (this.ClientSideWeakPassword(pwd)) {
25310             strength = 1;
25311         } else {
25312             strength = 0;
25313         }
25314         
25315         Roo.log('strength1: ' + strength);
25316         
25317         //var pm = this.trigger.child('div/div/div').dom;
25318         var pm = this.trigger.child('div/div');
25319         pm.removeClass(this.meterClass);
25320         pm.addClass(this.meterClass[strength]);
25321                 
25322         
25323         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25324                 
25325         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25326         
25327         this._lastPwd = pwd;
25328     },
25329     reset: function ()
25330     {
25331         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25332         
25333         this._lastPwd = '';
25334         
25335         var pm = this.trigger.child('div/div');
25336         pm.removeClass(this.meterClass);
25337         pm.addClass('roo-password-meter-grey');        
25338         
25339         
25340         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25341         
25342         pt.innerHTML = '';
25343         this.inputEl().dom.type='password';
25344     },
25345     // private
25346     validateValue: function (value)
25347     {
25348         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25349             return false;
25350         }
25351         if (value.length == 0) {
25352             if (this.allowBlank) {
25353                 this.clearInvalid();
25354                 return true;
25355             }
25356
25357             this.markInvalid(this.errors.PwdEmpty);
25358             this.errorMsg = this.errors.PwdEmpty;
25359             return false;
25360         }
25361         
25362         if(this.insecure){
25363             return true;
25364         }
25365         
25366         if (!value.match(/[\x21-\x7e]+/)) {
25367             this.markInvalid(this.errors.PwdBadChar);
25368             this.errorMsg = this.errors.PwdBadChar;
25369             return false;
25370         }
25371         if (value.length < 6) {
25372             this.markInvalid(this.errors.PwdShort);
25373             this.errorMsg = this.errors.PwdShort;
25374             return false;
25375         }
25376         if (value.length > 16) {
25377             this.markInvalid(this.errors.PwdLong);
25378             this.errorMsg = this.errors.PwdLong;
25379             return false;
25380         }
25381         var strength;
25382         if (this.ClientSideStrongPassword(value)) {
25383             strength = 3;
25384         } else if (this.ClientSideMediumPassword(value)) {
25385             strength = 2;
25386         } else if (this.ClientSideWeakPassword(value)) {
25387             strength = 1;
25388         } else {
25389             strength = 0;
25390         }
25391
25392         
25393         if (strength < 2) {
25394             //this.markInvalid(this.errors.TooWeak);
25395             this.errorMsg = this.errors.TooWeak;
25396             //return false;
25397         }
25398         
25399         
25400         console.log('strength2: ' + strength);
25401         
25402         //var pm = this.trigger.child('div/div/div').dom;
25403         
25404         var pm = this.trigger.child('div/div');
25405         pm.removeClass(this.meterClass);
25406         pm.addClass(this.meterClass[strength]);
25407                 
25408         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25409                 
25410         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25411         
25412         this.errorMsg = ''; 
25413         return true;
25414     },
25415     // private
25416     CharacterSetChecks: function (type)
25417     {
25418         this.type = type;
25419         this.fResult = false;
25420     },
25421     // private
25422     isctype: function (character, type)
25423     {
25424         switch (type) {  
25425             case this.kCapitalLetter:
25426                 if (character >= 'A' && character <= 'Z') {
25427                     return true;
25428                 }
25429                 break;
25430             
25431             case this.kSmallLetter:
25432                 if (character >= 'a' && character <= 'z') {
25433                     return true;
25434                 }
25435                 break;
25436             
25437             case this.kDigit:
25438                 if (character >= '0' && character <= '9') {
25439                     return true;
25440                 }
25441                 break;
25442             
25443             case this.kPunctuation:
25444                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25445                     return true;
25446                 }
25447                 break;
25448             
25449             default:
25450                 return false;
25451         }
25452
25453     },
25454     // private
25455     IsLongEnough: function (pwd, size)
25456     {
25457         return !(pwd == null || isNaN(size) || pwd.length < size);
25458     },
25459     // private
25460     SpansEnoughCharacterSets: function (word, nb)
25461     {
25462         if (!this.IsLongEnough(word, nb))
25463         {
25464             return false;
25465         }
25466
25467         var characterSetChecks = new Array(
25468             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25469             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25470         );
25471         
25472         for (var index = 0; index < word.length; ++index) {
25473             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25474                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25475                     characterSetChecks[nCharSet].fResult = true;
25476                     break;
25477                 }
25478             }
25479         }
25480
25481         var nCharSets = 0;
25482         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25483             if (characterSetChecks[nCharSet].fResult) {
25484                 ++nCharSets;
25485             }
25486         }
25487
25488         if (nCharSets < nb) {
25489             return false;
25490         }
25491         return true;
25492     },
25493     // private
25494     ClientSideStrongPassword: function (pwd)
25495     {
25496         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25497     },
25498     // private
25499     ClientSideMediumPassword: function (pwd)
25500     {
25501         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25502     },
25503     // private
25504     ClientSideWeakPassword: function (pwd)
25505     {
25506         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25507     }
25508           
25509 })//<script type="text/javascript">
25510
25511 /*
25512  * Based  Ext JS Library 1.1.1
25513  * Copyright(c) 2006-2007, Ext JS, LLC.
25514  * LGPL
25515  *
25516  */
25517  
25518 /**
25519  * @class Roo.HtmlEditorCore
25520  * @extends Roo.Component
25521  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25522  *
25523  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25524  */
25525
25526 Roo.HtmlEditorCore = function(config){
25527     
25528     
25529     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25530     
25531     
25532     this.addEvents({
25533         /**
25534          * @event initialize
25535          * Fires when the editor is fully initialized (including the iframe)
25536          * @param {Roo.HtmlEditorCore} this
25537          */
25538         initialize: true,
25539         /**
25540          * @event activate
25541          * Fires when the editor is first receives the focus. Any insertion must wait
25542          * until after this event.
25543          * @param {Roo.HtmlEditorCore} this
25544          */
25545         activate: true,
25546          /**
25547          * @event beforesync
25548          * Fires before the textarea is updated with content from the editor iframe. Return false
25549          * to cancel the sync.
25550          * @param {Roo.HtmlEditorCore} this
25551          * @param {String} html
25552          */
25553         beforesync: true,
25554          /**
25555          * @event beforepush
25556          * Fires before the iframe editor is updated with content from the textarea. Return false
25557          * to cancel the push.
25558          * @param {Roo.HtmlEditorCore} this
25559          * @param {String} html
25560          */
25561         beforepush: true,
25562          /**
25563          * @event sync
25564          * Fires when the textarea is updated with content from the editor iframe.
25565          * @param {Roo.HtmlEditorCore} this
25566          * @param {String} html
25567          */
25568         sync: true,
25569          /**
25570          * @event push
25571          * Fires when the iframe editor is updated with content from the textarea.
25572          * @param {Roo.HtmlEditorCore} this
25573          * @param {String} html
25574          */
25575         push: true,
25576         
25577         /**
25578          * @event editorevent
25579          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25580          * @param {Roo.HtmlEditorCore} this
25581          */
25582         editorevent: true
25583         
25584     });
25585     
25586     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25587     
25588     // defaults : white / black...
25589     this.applyBlacklists();
25590     
25591     
25592     
25593 };
25594
25595
25596 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25597
25598
25599      /**
25600      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25601      */
25602     
25603     owner : false,
25604     
25605      /**
25606      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25607      *                        Roo.resizable.
25608      */
25609     resizable : false,
25610      /**
25611      * @cfg {Number} height (in pixels)
25612      */   
25613     height: 300,
25614    /**
25615      * @cfg {Number} width (in pixels)
25616      */   
25617     width: 500,
25618     
25619     /**
25620      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25621      * 
25622      */
25623     stylesheets: false,
25624     
25625     /**
25626      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25627      */
25628     allowComments: false,
25629     // id of frame..
25630     frameId: false,
25631     
25632     // private properties
25633     validationEvent : false,
25634     deferHeight: true,
25635     initialized : false,
25636     activated : false,
25637     sourceEditMode : false,
25638     onFocus : Roo.emptyFn,
25639     iframePad:3,
25640     hideMode:'offsets',
25641     
25642     clearUp: true,
25643     
25644     // blacklist + whitelisted elements..
25645     black: false,
25646     white: false,
25647      
25648     bodyCls : '',
25649
25650     /**
25651      * Protected method that will not generally be called directly. It
25652      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25653      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25654      */
25655     getDocMarkup : function(){
25656         // body styles..
25657         var st = '';
25658         
25659         // inherit styels from page...?? 
25660         if (this.stylesheets === false) {
25661             
25662             Roo.get(document.head).select('style').each(function(node) {
25663                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25664             });
25665             
25666             Roo.get(document.head).select('link').each(function(node) { 
25667                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25668             });
25669             
25670         } else if (!this.stylesheets.length) {
25671                 // simple..
25672                 st = '<style type="text/css">' +
25673                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25674                    '</style>';
25675         } else {
25676             for (var i in this.stylesheets) { 
25677                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25678             }
25679             
25680         }
25681         
25682         st +=  '<style type="text/css">' +
25683             'IMG { cursor: pointer } ' +
25684         '</style>';
25685
25686         var cls = 'roo-htmleditor-body';
25687         
25688         if(this.bodyCls.length){
25689             cls += ' ' + this.bodyCls;
25690         }
25691         
25692         return '<html><head>' + st  +
25693             //<style type="text/css">' +
25694             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25695             //'</style>' +
25696             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25697     },
25698
25699     // private
25700     onRender : function(ct, position)
25701     {
25702         var _t = this;
25703         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25704         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25705         
25706         
25707         this.el.dom.style.border = '0 none';
25708         this.el.dom.setAttribute('tabIndex', -1);
25709         this.el.addClass('x-hidden hide');
25710         
25711         
25712         
25713         if(Roo.isIE){ // fix IE 1px bogus margin
25714             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25715         }
25716        
25717         
25718         this.frameId = Roo.id();
25719         
25720          
25721         
25722         var iframe = this.owner.wrap.createChild({
25723             tag: 'iframe',
25724             cls: 'form-control', // bootstrap..
25725             id: this.frameId,
25726             name: this.frameId,
25727             frameBorder : 'no',
25728             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25729         }, this.el
25730         );
25731         
25732         
25733         this.iframe = iframe.dom;
25734
25735          this.assignDocWin();
25736         
25737         this.doc.designMode = 'on';
25738        
25739         this.doc.open();
25740         this.doc.write(this.getDocMarkup());
25741         this.doc.close();
25742
25743         
25744         var task = { // must defer to wait for browser to be ready
25745             run : function(){
25746                 //console.log("run task?" + this.doc.readyState);
25747                 this.assignDocWin();
25748                 if(this.doc.body || this.doc.readyState == 'complete'){
25749                     try {
25750                         this.doc.designMode="on";
25751                     } catch (e) {
25752                         return;
25753                     }
25754                     Roo.TaskMgr.stop(task);
25755                     this.initEditor.defer(10, this);
25756                 }
25757             },
25758             interval : 10,
25759             duration: 10000,
25760             scope: this
25761         };
25762         Roo.TaskMgr.start(task);
25763
25764     },
25765
25766     // private
25767     onResize : function(w, h)
25768     {
25769          Roo.log('resize: ' +w + ',' + h );
25770         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25771         if(!this.iframe){
25772             return;
25773         }
25774         if(typeof w == 'number'){
25775             
25776             this.iframe.style.width = w + 'px';
25777         }
25778         if(typeof h == 'number'){
25779             
25780             this.iframe.style.height = h + 'px';
25781             if(this.doc){
25782                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25783             }
25784         }
25785         
25786     },
25787
25788     /**
25789      * Toggles the editor between standard and source edit mode.
25790      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25791      */
25792     toggleSourceEdit : function(sourceEditMode){
25793         
25794         this.sourceEditMode = sourceEditMode === true;
25795         
25796         if(this.sourceEditMode){
25797  
25798             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25799             
25800         }else{
25801             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25802             //this.iframe.className = '';
25803             this.deferFocus();
25804         }
25805         //this.setSize(this.owner.wrap.getSize());
25806         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25807     },
25808
25809     
25810   
25811
25812     /**
25813      * Protected method that will not generally be called directly. If you need/want
25814      * custom HTML cleanup, this is the method you should override.
25815      * @param {String} html The HTML to be cleaned
25816      * return {String} The cleaned HTML
25817      */
25818     cleanHtml : function(html){
25819         html = String(html);
25820         if(html.length > 5){
25821             if(Roo.isSafari){ // strip safari nonsense
25822                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25823             }
25824         }
25825         if(html == '&nbsp;'){
25826             html = '';
25827         }
25828         return html;
25829     },
25830
25831     /**
25832      * HTML Editor -> Textarea
25833      * Protected method that will not generally be called directly. Syncs the contents
25834      * of the editor iframe with the textarea.
25835      */
25836     syncValue : function(){
25837         if(this.initialized){
25838             var bd = (this.doc.body || this.doc.documentElement);
25839             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25840             var html = bd.innerHTML;
25841             if(Roo.isSafari){
25842                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25843                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25844                 if(m && m[1]){
25845                     html = '<div style="'+m[0]+'">' + html + '</div>';
25846                 }
25847             }
25848             html = this.cleanHtml(html);
25849             // fix up the special chars.. normaly like back quotes in word...
25850             // however we do not want to do this with chinese..
25851             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25852                 
25853                 var cc = match.charCodeAt();
25854
25855                 // Get the character value, handling surrogate pairs
25856                 if (match.length == 2) {
25857                     // It's a surrogate pair, calculate the Unicode code point
25858                     var high = match.charCodeAt(0) - 0xD800;
25859                     var low  = match.charCodeAt(1) - 0xDC00;
25860                     cc = (high * 0x400) + low + 0x10000;
25861                 }  else if (
25862                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25863                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25864                     (cc >= 0xf900 && cc < 0xfb00 )
25865                 ) {
25866                         return match;
25867                 }  
25868          
25869                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25870                 return "&#" + cc + ";";
25871                 
25872                 
25873             });
25874             
25875             
25876              
25877             if(this.owner.fireEvent('beforesync', this, html) !== false){
25878                 this.el.dom.value = html;
25879                 this.owner.fireEvent('sync', this, html);
25880             }
25881         }
25882     },
25883
25884     /**
25885      * Protected method that will not generally be called directly. Pushes the value of the textarea
25886      * into the iframe editor.
25887      */
25888     pushValue : function(){
25889         if(this.initialized){
25890             var v = this.el.dom.value.trim();
25891             
25892 //            if(v.length < 1){
25893 //                v = '&#160;';
25894 //            }
25895             
25896             if(this.owner.fireEvent('beforepush', this, v) !== false){
25897                 var d = (this.doc.body || this.doc.documentElement);
25898                 d.innerHTML = v;
25899                 this.cleanUpPaste();
25900                 this.el.dom.value = d.innerHTML;
25901                 this.owner.fireEvent('push', this, v);
25902             }
25903         }
25904     },
25905
25906     // private
25907     deferFocus : function(){
25908         this.focus.defer(10, this);
25909     },
25910
25911     // doc'ed in Field
25912     focus : function(){
25913         if(this.win && !this.sourceEditMode){
25914             this.win.focus();
25915         }else{
25916             this.el.focus();
25917         }
25918     },
25919     
25920     assignDocWin: function()
25921     {
25922         var iframe = this.iframe;
25923         
25924          if(Roo.isIE){
25925             this.doc = iframe.contentWindow.document;
25926             this.win = iframe.contentWindow;
25927         } else {
25928 //            if (!Roo.get(this.frameId)) {
25929 //                return;
25930 //            }
25931 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25932 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25933             
25934             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25935                 return;
25936             }
25937             
25938             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25939             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25940         }
25941     },
25942     
25943     // private
25944     initEditor : function(){
25945         //console.log("INIT EDITOR");
25946         this.assignDocWin();
25947         
25948         
25949         
25950         this.doc.designMode="on";
25951         this.doc.open();
25952         this.doc.write(this.getDocMarkup());
25953         this.doc.close();
25954         
25955         var dbody = (this.doc.body || this.doc.documentElement);
25956         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25957         // this copies styles from the containing element into thsi one..
25958         // not sure why we need all of this..
25959         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25960         
25961         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25962         //ss['background-attachment'] = 'fixed'; // w3c
25963         dbody.bgProperties = 'fixed'; // ie
25964         //Roo.DomHelper.applyStyles(dbody, ss);
25965         Roo.EventManager.on(this.doc, {
25966             //'mousedown': this.onEditorEvent,
25967             'mouseup': this.onEditorEvent,
25968             'dblclick': this.onEditorEvent,
25969             'click': this.onEditorEvent,
25970             'keyup': this.onEditorEvent,
25971             buffer:100,
25972             scope: this
25973         });
25974         if(Roo.isGecko){
25975             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25976         }
25977         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25978             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25979         }
25980         this.initialized = true;
25981
25982         this.owner.fireEvent('initialize', this);
25983         this.pushValue();
25984     },
25985
25986     // private
25987     onDestroy : function(){
25988         
25989         
25990         
25991         if(this.rendered){
25992             
25993             //for (var i =0; i < this.toolbars.length;i++) {
25994             //    // fixme - ask toolbars for heights?
25995             //    this.toolbars[i].onDestroy();
25996            // }
25997             
25998             //this.wrap.dom.innerHTML = '';
25999             //this.wrap.remove();
26000         }
26001     },
26002
26003     // private
26004     onFirstFocus : function(){
26005         
26006         this.assignDocWin();
26007         
26008         
26009         this.activated = true;
26010          
26011     
26012         if(Roo.isGecko){ // prevent silly gecko errors
26013             this.win.focus();
26014             var s = this.win.getSelection();
26015             if(!s.focusNode || s.focusNode.nodeType != 3){
26016                 var r = s.getRangeAt(0);
26017                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26018                 r.collapse(true);
26019                 this.deferFocus();
26020             }
26021             try{
26022                 this.execCmd('useCSS', true);
26023                 this.execCmd('styleWithCSS', false);
26024             }catch(e){}
26025         }
26026         this.owner.fireEvent('activate', this);
26027     },
26028
26029     // private
26030     adjustFont: function(btn){
26031         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26032         //if(Roo.isSafari){ // safari
26033         //    adjust *= 2;
26034        // }
26035         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26036         if(Roo.isSafari){ // safari
26037             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26038             v =  (v < 10) ? 10 : v;
26039             v =  (v > 48) ? 48 : v;
26040             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26041             
26042         }
26043         
26044         
26045         v = Math.max(1, v+adjust);
26046         
26047         this.execCmd('FontSize', v  );
26048     },
26049
26050     onEditorEvent : function(e)
26051     {
26052         this.owner.fireEvent('editorevent', this, e);
26053       //  this.updateToolbar();
26054         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26055     },
26056
26057     insertTag : function(tg)
26058     {
26059         // could be a bit smarter... -> wrap the current selected tRoo..
26060         if (tg.toLowerCase() == 'span' ||
26061             tg.toLowerCase() == 'code' ||
26062             tg.toLowerCase() == 'sup' ||
26063             tg.toLowerCase() == 'sub' 
26064             ) {
26065             
26066             range = this.createRange(this.getSelection());
26067             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26068             wrappingNode.appendChild(range.extractContents());
26069             range.insertNode(wrappingNode);
26070
26071             return;
26072             
26073             
26074             
26075         }
26076         this.execCmd("formatblock",   tg);
26077         
26078     },
26079     
26080     insertText : function(txt)
26081     {
26082         
26083         
26084         var range = this.createRange();
26085         range.deleteContents();
26086                //alert(Sender.getAttribute('label'));
26087                
26088         range.insertNode(this.doc.createTextNode(txt));
26089     } ,
26090     
26091      
26092
26093     /**
26094      * Executes a Midas editor command on the editor document and performs necessary focus and
26095      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26096      * @param {String} cmd The Midas command
26097      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26098      */
26099     relayCmd : function(cmd, value){
26100         this.win.focus();
26101         this.execCmd(cmd, value);
26102         this.owner.fireEvent('editorevent', this);
26103         //this.updateToolbar();
26104         this.owner.deferFocus();
26105     },
26106
26107     /**
26108      * Executes a Midas editor command directly on the editor document.
26109      * For visual commands, you should use {@link #relayCmd} instead.
26110      * <b>This should only be called after the editor is initialized.</b>
26111      * @param {String} cmd The Midas command
26112      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26113      */
26114     execCmd : function(cmd, value){
26115         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26116         this.syncValue();
26117     },
26118  
26119  
26120    
26121     /**
26122      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26123      * to insert tRoo.
26124      * @param {String} text | dom node.. 
26125      */
26126     insertAtCursor : function(text)
26127     {
26128         
26129         if(!this.activated){
26130             return;
26131         }
26132         /*
26133         if(Roo.isIE){
26134             this.win.focus();
26135             var r = this.doc.selection.createRange();
26136             if(r){
26137                 r.collapse(true);
26138                 r.pasteHTML(text);
26139                 this.syncValue();
26140                 this.deferFocus();
26141             
26142             }
26143             return;
26144         }
26145         */
26146         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26147             this.win.focus();
26148             
26149             
26150             // from jquery ui (MIT licenced)
26151             var range, node;
26152             var win = this.win;
26153             
26154             if (win.getSelection && win.getSelection().getRangeAt) {
26155                 range = win.getSelection().getRangeAt(0);
26156                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26157                 range.insertNode(node);
26158             } else if (win.document.selection && win.document.selection.createRange) {
26159                 // no firefox support
26160                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26161                 win.document.selection.createRange().pasteHTML(txt);
26162             } else {
26163                 // no firefox support
26164                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26165                 this.execCmd('InsertHTML', txt);
26166             } 
26167             
26168             this.syncValue();
26169             
26170             this.deferFocus();
26171         }
26172     },
26173  // private
26174     mozKeyPress : function(e){
26175         if(e.ctrlKey){
26176             var c = e.getCharCode(), cmd;
26177           
26178             if(c > 0){
26179                 c = String.fromCharCode(c).toLowerCase();
26180                 switch(c){
26181                     case 'b':
26182                         cmd = 'bold';
26183                         break;
26184                     case 'i':
26185                         cmd = 'italic';
26186                         break;
26187                     
26188                     case 'u':
26189                         cmd = 'underline';
26190                         break;
26191                     
26192                     case 'v':
26193                         this.cleanUpPaste.defer(100, this);
26194                         return;
26195                         
26196                 }
26197                 if(cmd){
26198                     this.win.focus();
26199                     this.execCmd(cmd);
26200                     this.deferFocus();
26201                     e.preventDefault();
26202                 }
26203                 
26204             }
26205         }
26206     },
26207
26208     // private
26209     fixKeys : function(){ // load time branching for fastest keydown performance
26210         if(Roo.isIE){
26211             return function(e){
26212                 var k = e.getKey(), r;
26213                 if(k == e.TAB){
26214                     e.stopEvent();
26215                     r = this.doc.selection.createRange();
26216                     if(r){
26217                         r.collapse(true);
26218                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26219                         this.deferFocus();
26220                     }
26221                     return;
26222                 }
26223                 
26224                 if(k == e.ENTER){
26225                     r = this.doc.selection.createRange();
26226                     if(r){
26227                         var target = r.parentElement();
26228                         if(!target || target.tagName.toLowerCase() != 'li'){
26229                             e.stopEvent();
26230                             r.pasteHTML('<br />');
26231                             r.collapse(false);
26232                             r.select();
26233                         }
26234                     }
26235                 }
26236                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26237                     this.cleanUpPaste.defer(100, this);
26238                     return;
26239                 }
26240                 
26241                 
26242             };
26243         }else if(Roo.isOpera){
26244             return function(e){
26245                 var k = e.getKey();
26246                 if(k == e.TAB){
26247                     e.stopEvent();
26248                     this.win.focus();
26249                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26250                     this.deferFocus();
26251                 }
26252                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26253                     this.cleanUpPaste.defer(100, this);
26254                     return;
26255                 }
26256                 
26257             };
26258         }else if(Roo.isSafari){
26259             return function(e){
26260                 var k = e.getKey();
26261                 
26262                 if(k == e.TAB){
26263                     e.stopEvent();
26264                     this.execCmd('InsertText','\t');
26265                     this.deferFocus();
26266                     return;
26267                 }
26268                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26269                     this.cleanUpPaste.defer(100, this);
26270                     return;
26271                 }
26272                 
26273              };
26274         }
26275     }(),
26276     
26277     getAllAncestors: function()
26278     {
26279         var p = this.getSelectedNode();
26280         var a = [];
26281         if (!p) {
26282             a.push(p); // push blank onto stack..
26283             p = this.getParentElement();
26284         }
26285         
26286         
26287         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26288             a.push(p);
26289             p = p.parentNode;
26290         }
26291         a.push(this.doc.body);
26292         return a;
26293     },
26294     lastSel : false,
26295     lastSelNode : false,
26296     
26297     
26298     getSelection : function() 
26299     {
26300         this.assignDocWin();
26301         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26302     },
26303     
26304     getSelectedNode: function() 
26305     {
26306         // this may only work on Gecko!!!
26307         
26308         // should we cache this!!!!
26309         
26310         
26311         
26312          
26313         var range = this.createRange(this.getSelection()).cloneRange();
26314         
26315         if (Roo.isIE) {
26316             var parent = range.parentElement();
26317             while (true) {
26318                 var testRange = range.duplicate();
26319                 testRange.moveToElementText(parent);
26320                 if (testRange.inRange(range)) {
26321                     break;
26322                 }
26323                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26324                     break;
26325                 }
26326                 parent = parent.parentElement;
26327             }
26328             return parent;
26329         }
26330         
26331         // is ancestor a text element.
26332         var ac =  range.commonAncestorContainer;
26333         if (ac.nodeType == 3) {
26334             ac = ac.parentNode;
26335         }
26336         
26337         var ar = ac.childNodes;
26338          
26339         var nodes = [];
26340         var other_nodes = [];
26341         var has_other_nodes = false;
26342         for (var i=0;i<ar.length;i++) {
26343             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26344                 continue;
26345             }
26346             // fullly contained node.
26347             
26348             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26349                 nodes.push(ar[i]);
26350                 continue;
26351             }
26352             
26353             // probably selected..
26354             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26355                 other_nodes.push(ar[i]);
26356                 continue;
26357             }
26358             // outer..
26359             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26360                 continue;
26361             }
26362             
26363             
26364             has_other_nodes = true;
26365         }
26366         if (!nodes.length && other_nodes.length) {
26367             nodes= other_nodes;
26368         }
26369         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26370             return false;
26371         }
26372         
26373         return nodes[0];
26374     },
26375     createRange: function(sel)
26376     {
26377         // this has strange effects when using with 
26378         // top toolbar - not sure if it's a great idea.
26379         //this.editor.contentWindow.focus();
26380         if (typeof sel != "undefined") {
26381             try {
26382                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26383             } catch(e) {
26384                 return this.doc.createRange();
26385             }
26386         } else {
26387             return this.doc.createRange();
26388         }
26389     },
26390     getParentElement: function()
26391     {
26392         
26393         this.assignDocWin();
26394         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26395         
26396         var range = this.createRange(sel);
26397          
26398         try {
26399             var p = range.commonAncestorContainer;
26400             while (p.nodeType == 3) { // text node
26401                 p = p.parentNode;
26402             }
26403             return p;
26404         } catch (e) {
26405             return null;
26406         }
26407     
26408     },
26409     /***
26410      *
26411      * Range intersection.. the hard stuff...
26412      *  '-1' = before
26413      *  '0' = hits..
26414      *  '1' = after.
26415      *         [ -- selected range --- ]
26416      *   [fail]                        [fail]
26417      *
26418      *    basically..
26419      *      if end is before start or  hits it. fail.
26420      *      if start is after end or hits it fail.
26421      *
26422      *   if either hits (but other is outside. - then it's not 
26423      *   
26424      *    
26425      **/
26426     
26427     
26428     // @see http://www.thismuchiknow.co.uk/?p=64.
26429     rangeIntersectsNode : function(range, node)
26430     {
26431         var nodeRange = node.ownerDocument.createRange();
26432         try {
26433             nodeRange.selectNode(node);
26434         } catch (e) {
26435             nodeRange.selectNodeContents(node);
26436         }
26437     
26438         var rangeStartRange = range.cloneRange();
26439         rangeStartRange.collapse(true);
26440     
26441         var rangeEndRange = range.cloneRange();
26442         rangeEndRange.collapse(false);
26443     
26444         var nodeStartRange = nodeRange.cloneRange();
26445         nodeStartRange.collapse(true);
26446     
26447         var nodeEndRange = nodeRange.cloneRange();
26448         nodeEndRange.collapse(false);
26449     
26450         return rangeStartRange.compareBoundaryPoints(
26451                  Range.START_TO_START, nodeEndRange) == -1 &&
26452                rangeEndRange.compareBoundaryPoints(
26453                  Range.START_TO_START, nodeStartRange) == 1;
26454         
26455          
26456     },
26457     rangeCompareNode : function(range, node)
26458     {
26459         var nodeRange = node.ownerDocument.createRange();
26460         try {
26461             nodeRange.selectNode(node);
26462         } catch (e) {
26463             nodeRange.selectNodeContents(node);
26464         }
26465         
26466         
26467         range.collapse(true);
26468     
26469         nodeRange.collapse(true);
26470      
26471         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26472         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26473          
26474         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26475         
26476         var nodeIsBefore   =  ss == 1;
26477         var nodeIsAfter    = ee == -1;
26478         
26479         if (nodeIsBefore && nodeIsAfter) {
26480             return 0; // outer
26481         }
26482         if (!nodeIsBefore && nodeIsAfter) {
26483             return 1; //right trailed.
26484         }
26485         
26486         if (nodeIsBefore && !nodeIsAfter) {
26487             return 2;  // left trailed.
26488         }
26489         // fully contined.
26490         return 3;
26491     },
26492
26493     // private? - in a new class?
26494     cleanUpPaste :  function()
26495     {
26496         // cleans up the whole document..
26497         Roo.log('cleanuppaste');
26498         
26499         this.cleanUpChildren(this.doc.body);
26500         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26501         if (clean != this.doc.body.innerHTML) {
26502             this.doc.body.innerHTML = clean;
26503         }
26504         
26505     },
26506     
26507     cleanWordChars : function(input) {// change the chars to hex code
26508         var he = Roo.HtmlEditorCore;
26509         
26510         var output = input;
26511         Roo.each(he.swapCodes, function(sw) { 
26512             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26513             
26514             output = output.replace(swapper, sw[1]);
26515         });
26516         
26517         return output;
26518     },
26519     
26520     
26521     cleanUpChildren : function (n)
26522     {
26523         if (!n.childNodes.length) {
26524             return;
26525         }
26526         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26527            this.cleanUpChild(n.childNodes[i]);
26528         }
26529     },
26530     
26531     
26532         
26533     
26534     cleanUpChild : function (node)
26535     {
26536         var ed = this;
26537         //console.log(node);
26538         if (node.nodeName == "#text") {
26539             // clean up silly Windows -- stuff?
26540             return; 
26541         }
26542         if (node.nodeName == "#comment") {
26543             if (!this.allowComments) {
26544                 node.parentNode.removeChild(node);
26545             }
26546             // clean up silly Windows -- stuff?
26547             return; 
26548         }
26549         var lcname = node.tagName.toLowerCase();
26550         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26551         // whitelist of tags..
26552         
26553         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26554             // remove node.
26555             node.parentNode.removeChild(node);
26556             return;
26557             
26558         }
26559         
26560         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26561         
26562         // spans with no attributes - just remove them..
26563         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26564             remove_keep_children = true;
26565         }
26566         
26567         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26568         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26569         
26570         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26571         //    remove_keep_children = true;
26572         //}
26573         
26574         if (remove_keep_children) {
26575             this.cleanUpChildren(node);
26576             // inserts everything just before this node...
26577             while (node.childNodes.length) {
26578                 var cn = node.childNodes[0];
26579                 node.removeChild(cn);
26580                 node.parentNode.insertBefore(cn, node);
26581             }
26582             node.parentNode.removeChild(node);
26583             return;
26584         }
26585         
26586         if (!node.attributes || !node.attributes.length) {
26587             
26588           
26589             
26590             
26591             this.cleanUpChildren(node);
26592             return;
26593         }
26594         
26595         function cleanAttr(n,v)
26596         {
26597             
26598             if (v.match(/^\./) || v.match(/^\//)) {
26599                 return;
26600             }
26601             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26602                 return;
26603             }
26604             if (v.match(/^#/)) {
26605                 return;
26606             }
26607             if (v.match(/^\{/)) { // allow template editing.
26608                 return;
26609             }
26610 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26611             node.removeAttribute(n);
26612             
26613         }
26614         
26615         var cwhite = this.cwhite;
26616         var cblack = this.cblack;
26617             
26618         function cleanStyle(n,v)
26619         {
26620             if (v.match(/expression/)) { //XSS?? should we even bother..
26621                 node.removeAttribute(n);
26622                 return;
26623             }
26624             
26625             var parts = v.split(/;/);
26626             var clean = [];
26627             
26628             Roo.each(parts, function(p) {
26629                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26630                 if (!p.length) {
26631                     return true;
26632                 }
26633                 var l = p.split(':').shift().replace(/\s+/g,'');
26634                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26635                 
26636                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26637 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26638                     //node.removeAttribute(n);
26639                     return true;
26640                 }
26641                 //Roo.log()
26642                 // only allow 'c whitelisted system attributes'
26643                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26644 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26645                     //node.removeAttribute(n);
26646                     return true;
26647                 }
26648                 
26649                 
26650                  
26651                 
26652                 clean.push(p);
26653                 return true;
26654             });
26655             if (clean.length) { 
26656                 node.setAttribute(n, clean.join(';'));
26657             } else {
26658                 node.removeAttribute(n);
26659             }
26660             
26661         }
26662         
26663         
26664         for (var i = node.attributes.length-1; i > -1 ; i--) {
26665             var a = node.attributes[i];
26666             //console.log(a);
26667             
26668             if (a.name.toLowerCase().substr(0,2)=='on')  {
26669                 node.removeAttribute(a.name);
26670                 continue;
26671             }
26672             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26673                 node.removeAttribute(a.name);
26674                 continue;
26675             }
26676             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26677                 cleanAttr(a.name,a.value); // fixme..
26678                 continue;
26679             }
26680             if (a.name == 'style') {
26681                 cleanStyle(a.name,a.value);
26682                 continue;
26683             }
26684             /// clean up MS crap..
26685             // tecnically this should be a list of valid class'es..
26686             
26687             
26688             if (a.name == 'class') {
26689                 if (a.value.match(/^Mso/)) {
26690                     node.removeAttribute('class');
26691                 }
26692                 
26693                 if (a.value.match(/^body$/)) {
26694                     node.removeAttribute('class');
26695                 }
26696                 continue;
26697             }
26698             
26699             // style cleanup!?
26700             // class cleanup?
26701             
26702         }
26703         
26704         
26705         this.cleanUpChildren(node);
26706         
26707         
26708     },
26709     
26710     /**
26711      * Clean up MS wordisms...
26712      */
26713     cleanWord : function(node)
26714     {
26715         if (!node) {
26716             this.cleanWord(this.doc.body);
26717             return;
26718         }
26719         
26720         if(
26721                 node.nodeName == 'SPAN' &&
26722                 !node.hasAttributes() &&
26723                 node.childNodes.length == 1 &&
26724                 node.firstChild.nodeName == "#text"  
26725         ) {
26726             var textNode = node.firstChild;
26727             node.removeChild(textNode);
26728             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26729                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26730             }
26731             node.parentNode.insertBefore(textNode, node);
26732             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26733                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26734             }
26735             node.parentNode.removeChild(node);
26736         }
26737         
26738         if (node.nodeName == "#text") {
26739             // clean up silly Windows -- stuff?
26740             return; 
26741         }
26742         if (node.nodeName == "#comment") {
26743             node.parentNode.removeChild(node);
26744             // clean up silly Windows -- stuff?
26745             return; 
26746         }
26747         
26748         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26749             node.parentNode.removeChild(node);
26750             return;
26751         }
26752         //Roo.log(node.tagName);
26753         // remove - but keep children..
26754         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26755             //Roo.log('-- removed');
26756             while (node.childNodes.length) {
26757                 var cn = node.childNodes[0];
26758                 node.removeChild(cn);
26759                 node.parentNode.insertBefore(cn, node);
26760                 // move node to parent - and clean it..
26761                 this.cleanWord(cn);
26762             }
26763             node.parentNode.removeChild(node);
26764             /// no need to iterate chidlren = it's got none..
26765             //this.iterateChildren(node, this.cleanWord);
26766             return;
26767         }
26768         // clean styles
26769         if (node.className.length) {
26770             
26771             var cn = node.className.split(/\W+/);
26772             var cna = [];
26773             Roo.each(cn, function(cls) {
26774                 if (cls.match(/Mso[a-zA-Z]+/)) {
26775                     return;
26776                 }
26777                 cna.push(cls);
26778             });
26779             node.className = cna.length ? cna.join(' ') : '';
26780             if (!cna.length) {
26781                 node.removeAttribute("class");
26782             }
26783         }
26784         
26785         if (node.hasAttribute("lang")) {
26786             node.removeAttribute("lang");
26787         }
26788         
26789         if (node.hasAttribute("style")) {
26790             
26791             var styles = node.getAttribute("style").split(";");
26792             var nstyle = [];
26793             Roo.each(styles, function(s) {
26794                 if (!s.match(/:/)) {
26795                     return;
26796                 }
26797                 var kv = s.split(":");
26798                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26799                     return;
26800                 }
26801                 // what ever is left... we allow.
26802                 nstyle.push(s);
26803             });
26804             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26805             if (!nstyle.length) {
26806                 node.removeAttribute('style');
26807             }
26808         }
26809         this.iterateChildren(node, this.cleanWord);
26810         
26811         
26812         
26813     },
26814     /**
26815      * iterateChildren of a Node, calling fn each time, using this as the scole..
26816      * @param {DomNode} node node to iterate children of.
26817      * @param {Function} fn method of this class to call on each item.
26818      */
26819     iterateChildren : function(node, fn)
26820     {
26821         if (!node.childNodes.length) {
26822                 return;
26823         }
26824         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26825            fn.call(this, node.childNodes[i])
26826         }
26827     },
26828     
26829     
26830     /**
26831      * cleanTableWidths.
26832      *
26833      * Quite often pasting from word etc.. results in tables with column and widths.
26834      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26835      *
26836      */
26837     cleanTableWidths : function(node)
26838     {
26839          
26840          
26841         if (!node) {
26842             this.cleanTableWidths(this.doc.body);
26843             return;
26844         }
26845         
26846         // ignore list...
26847         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26848             return; 
26849         }
26850         Roo.log(node.tagName);
26851         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26852             this.iterateChildren(node, this.cleanTableWidths);
26853             return;
26854         }
26855         if (node.hasAttribute('width')) {
26856             node.removeAttribute('width');
26857         }
26858         
26859          
26860         if (node.hasAttribute("style")) {
26861             // pretty basic...
26862             
26863             var styles = node.getAttribute("style").split(";");
26864             var nstyle = [];
26865             Roo.each(styles, function(s) {
26866                 if (!s.match(/:/)) {
26867                     return;
26868                 }
26869                 var kv = s.split(":");
26870                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26871                     return;
26872                 }
26873                 // what ever is left... we allow.
26874                 nstyle.push(s);
26875             });
26876             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26877             if (!nstyle.length) {
26878                 node.removeAttribute('style');
26879             }
26880         }
26881         
26882         this.iterateChildren(node, this.cleanTableWidths);
26883         
26884         
26885     },
26886     
26887     
26888     
26889     
26890     domToHTML : function(currentElement, depth, nopadtext) {
26891         
26892         depth = depth || 0;
26893         nopadtext = nopadtext || false;
26894     
26895         if (!currentElement) {
26896             return this.domToHTML(this.doc.body);
26897         }
26898         
26899         //Roo.log(currentElement);
26900         var j;
26901         var allText = false;
26902         var nodeName = currentElement.nodeName;
26903         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26904         
26905         if  (nodeName == '#text') {
26906             
26907             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26908         }
26909         
26910         
26911         var ret = '';
26912         if (nodeName != 'BODY') {
26913              
26914             var i = 0;
26915             // Prints the node tagName, such as <A>, <IMG>, etc
26916             if (tagName) {
26917                 var attr = [];
26918                 for(i = 0; i < currentElement.attributes.length;i++) {
26919                     // quoting?
26920                     var aname = currentElement.attributes.item(i).name;
26921                     if (!currentElement.attributes.item(i).value.length) {
26922                         continue;
26923                     }
26924                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26925                 }
26926                 
26927                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26928             } 
26929             else {
26930                 
26931                 // eack
26932             }
26933         } else {
26934             tagName = false;
26935         }
26936         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26937             return ret;
26938         }
26939         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26940             nopadtext = true;
26941         }
26942         
26943         
26944         // Traverse the tree
26945         i = 0;
26946         var currentElementChild = currentElement.childNodes.item(i);
26947         var allText = true;
26948         var innerHTML  = '';
26949         lastnode = '';
26950         while (currentElementChild) {
26951             // Formatting code (indent the tree so it looks nice on the screen)
26952             var nopad = nopadtext;
26953             if (lastnode == 'SPAN') {
26954                 nopad  = true;
26955             }
26956             // text
26957             if  (currentElementChild.nodeName == '#text') {
26958                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26959                 toadd = nopadtext ? toadd : toadd.trim();
26960                 if (!nopad && toadd.length > 80) {
26961                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26962                 }
26963                 innerHTML  += toadd;
26964                 
26965                 i++;
26966                 currentElementChild = currentElement.childNodes.item(i);
26967                 lastNode = '';
26968                 continue;
26969             }
26970             allText = false;
26971             
26972             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26973                 
26974             // Recursively traverse the tree structure of the child node
26975             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26976             lastnode = currentElementChild.nodeName;
26977             i++;
26978             currentElementChild=currentElement.childNodes.item(i);
26979         }
26980         
26981         ret += innerHTML;
26982         
26983         if (!allText) {
26984                 // The remaining code is mostly for formatting the tree
26985             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26986         }
26987         
26988         
26989         if (tagName) {
26990             ret+= "</"+tagName+">";
26991         }
26992         return ret;
26993         
26994     },
26995         
26996     applyBlacklists : function()
26997     {
26998         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26999         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27000         
27001         this.white = [];
27002         this.black = [];
27003         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27004             if (b.indexOf(tag) > -1) {
27005                 return;
27006             }
27007             this.white.push(tag);
27008             
27009         }, this);
27010         
27011         Roo.each(w, function(tag) {
27012             if (b.indexOf(tag) > -1) {
27013                 return;
27014             }
27015             if (this.white.indexOf(tag) > -1) {
27016                 return;
27017             }
27018             this.white.push(tag);
27019             
27020         }, this);
27021         
27022         
27023         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27024             if (w.indexOf(tag) > -1) {
27025                 return;
27026             }
27027             this.black.push(tag);
27028             
27029         }, this);
27030         
27031         Roo.each(b, function(tag) {
27032             if (w.indexOf(tag) > -1) {
27033                 return;
27034             }
27035             if (this.black.indexOf(tag) > -1) {
27036                 return;
27037             }
27038             this.black.push(tag);
27039             
27040         }, this);
27041         
27042         
27043         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27044         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27045         
27046         this.cwhite = [];
27047         this.cblack = [];
27048         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27049             if (b.indexOf(tag) > -1) {
27050                 return;
27051             }
27052             this.cwhite.push(tag);
27053             
27054         }, this);
27055         
27056         Roo.each(w, function(tag) {
27057             if (b.indexOf(tag) > -1) {
27058                 return;
27059             }
27060             if (this.cwhite.indexOf(tag) > -1) {
27061                 return;
27062             }
27063             this.cwhite.push(tag);
27064             
27065         }, this);
27066         
27067         
27068         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27069             if (w.indexOf(tag) > -1) {
27070                 return;
27071             }
27072             this.cblack.push(tag);
27073             
27074         }, this);
27075         
27076         Roo.each(b, function(tag) {
27077             if (w.indexOf(tag) > -1) {
27078                 return;
27079             }
27080             if (this.cblack.indexOf(tag) > -1) {
27081                 return;
27082             }
27083             this.cblack.push(tag);
27084             
27085         }, this);
27086     },
27087     
27088     setStylesheets : function(stylesheets)
27089     {
27090         if(typeof(stylesheets) == 'string'){
27091             Roo.get(this.iframe.contentDocument.head).createChild({
27092                 tag : 'link',
27093                 rel : 'stylesheet',
27094                 type : 'text/css',
27095                 href : stylesheets
27096             });
27097             
27098             return;
27099         }
27100         var _this = this;
27101      
27102         Roo.each(stylesheets, function(s) {
27103             if(!s.length){
27104                 return;
27105             }
27106             
27107             Roo.get(_this.iframe.contentDocument.head).createChild({
27108                 tag : 'link',
27109                 rel : 'stylesheet',
27110                 type : 'text/css',
27111                 href : s
27112             });
27113         });
27114
27115         
27116     },
27117     
27118     removeStylesheets : function()
27119     {
27120         var _this = this;
27121         
27122         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27123             s.remove();
27124         });
27125     },
27126     
27127     setStyle : function(style)
27128     {
27129         Roo.get(this.iframe.contentDocument.head).createChild({
27130             tag : 'style',
27131             type : 'text/css',
27132             html : style
27133         });
27134
27135         return;
27136     }
27137     
27138     // hide stuff that is not compatible
27139     /**
27140      * @event blur
27141      * @hide
27142      */
27143     /**
27144      * @event change
27145      * @hide
27146      */
27147     /**
27148      * @event focus
27149      * @hide
27150      */
27151     /**
27152      * @event specialkey
27153      * @hide
27154      */
27155     /**
27156      * @cfg {String} fieldClass @hide
27157      */
27158     /**
27159      * @cfg {String} focusClass @hide
27160      */
27161     /**
27162      * @cfg {String} autoCreate @hide
27163      */
27164     /**
27165      * @cfg {String} inputType @hide
27166      */
27167     /**
27168      * @cfg {String} invalidClass @hide
27169      */
27170     /**
27171      * @cfg {String} invalidText @hide
27172      */
27173     /**
27174      * @cfg {String} msgFx @hide
27175      */
27176     /**
27177      * @cfg {String} validateOnBlur @hide
27178      */
27179 });
27180
27181 Roo.HtmlEditorCore.white = [
27182         'area', 'br', 'img', 'input', 'hr', 'wbr',
27183         
27184        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27185        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27186        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27187        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27188        'table',   'ul',         'xmp', 
27189        
27190        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27191       'thead',   'tr', 
27192      
27193       'dir', 'menu', 'ol', 'ul', 'dl',
27194        
27195       'embed',  'object'
27196 ];
27197
27198
27199 Roo.HtmlEditorCore.black = [
27200     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27201         'applet', // 
27202         'base',   'basefont', 'bgsound', 'blink',  'body', 
27203         'frame',  'frameset', 'head',    'html',   'ilayer', 
27204         'iframe', 'layer',  'link',     'meta',    'object',   
27205         'script', 'style' ,'title',  'xml' // clean later..
27206 ];
27207 Roo.HtmlEditorCore.clean = [
27208     'script', 'style', 'title', 'xml'
27209 ];
27210 Roo.HtmlEditorCore.remove = [
27211     'font'
27212 ];
27213 // attributes..
27214
27215 Roo.HtmlEditorCore.ablack = [
27216     'on'
27217 ];
27218     
27219 Roo.HtmlEditorCore.aclean = [ 
27220     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27221 ];
27222
27223 // protocols..
27224 Roo.HtmlEditorCore.pwhite= [
27225         'http',  'https',  'mailto'
27226 ];
27227
27228 // white listed style attributes.
27229 Roo.HtmlEditorCore.cwhite= [
27230       //  'text-align', /// default is to allow most things..
27231       
27232          
27233 //        'font-size'//??
27234 ];
27235
27236 // black listed style attributes.
27237 Roo.HtmlEditorCore.cblack= [
27238       //  'font-size' -- this can be set by the project 
27239 ];
27240
27241
27242 Roo.HtmlEditorCore.swapCodes   =[ 
27243     [    8211, "&#8211;" ], 
27244     [    8212, "&#8212;" ], 
27245     [    8216,  "'" ],  
27246     [    8217, "'" ],  
27247     [    8220, '"' ],  
27248     [    8221, '"' ],  
27249     [    8226, "*" ],  
27250     [    8230, "..." ]
27251 ]; 
27252
27253     /*
27254  * - LGPL
27255  *
27256  * HtmlEditor
27257  * 
27258  */
27259
27260 /**
27261  * @class Roo.bootstrap.HtmlEditor
27262  * @extends Roo.bootstrap.TextArea
27263  * Bootstrap HtmlEditor class
27264
27265  * @constructor
27266  * Create a new HtmlEditor
27267  * @param {Object} config The config object
27268  */
27269
27270 Roo.bootstrap.HtmlEditor = function(config){
27271     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27272     if (!this.toolbars) {
27273         this.toolbars = [];
27274     }
27275     
27276     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27277     this.addEvents({
27278             /**
27279              * @event initialize
27280              * Fires when the editor is fully initialized (including the iframe)
27281              * @param {HtmlEditor} this
27282              */
27283             initialize: true,
27284             /**
27285              * @event activate
27286              * Fires when the editor is first receives the focus. Any insertion must wait
27287              * until after this event.
27288              * @param {HtmlEditor} this
27289              */
27290             activate: true,
27291              /**
27292              * @event beforesync
27293              * Fires before the textarea is updated with content from the editor iframe. Return false
27294              * to cancel the sync.
27295              * @param {HtmlEditor} this
27296              * @param {String} html
27297              */
27298             beforesync: true,
27299              /**
27300              * @event beforepush
27301              * Fires before the iframe editor is updated with content from the textarea. Return false
27302              * to cancel the push.
27303              * @param {HtmlEditor} this
27304              * @param {String} html
27305              */
27306             beforepush: true,
27307              /**
27308              * @event sync
27309              * Fires when the textarea is updated with content from the editor iframe.
27310              * @param {HtmlEditor} this
27311              * @param {String} html
27312              */
27313             sync: true,
27314              /**
27315              * @event push
27316              * Fires when the iframe editor is updated with content from the textarea.
27317              * @param {HtmlEditor} this
27318              * @param {String} html
27319              */
27320             push: true,
27321              /**
27322              * @event editmodechange
27323              * Fires when the editor switches edit modes
27324              * @param {HtmlEditor} this
27325              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27326              */
27327             editmodechange: true,
27328             /**
27329              * @event editorevent
27330              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27331              * @param {HtmlEditor} this
27332              */
27333             editorevent: true,
27334             /**
27335              * @event firstfocus
27336              * Fires when on first focus - needed by toolbars..
27337              * @param {HtmlEditor} this
27338              */
27339             firstfocus: true,
27340             /**
27341              * @event autosave
27342              * Auto save the htmlEditor value as a file into Events
27343              * @param {HtmlEditor} this
27344              */
27345             autosave: true,
27346             /**
27347              * @event savedpreview
27348              * preview the saved version of htmlEditor
27349              * @param {HtmlEditor} this
27350              */
27351             savedpreview: true
27352         });
27353 };
27354
27355
27356 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27357     
27358     
27359       /**
27360      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27361      */
27362     toolbars : false,
27363     
27364      /**
27365     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27366     */
27367     btns : [],
27368    
27369      /**
27370      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27371      *                        Roo.resizable.
27372      */
27373     resizable : false,
27374      /**
27375      * @cfg {Number} height (in pixels)
27376      */   
27377     height: 300,
27378    /**
27379      * @cfg {Number} width (in pixels)
27380      */   
27381     width: false,
27382     
27383     /**
27384      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27385      * 
27386      */
27387     stylesheets: false,
27388     
27389     // id of frame..
27390     frameId: false,
27391     
27392     // private properties
27393     validationEvent : false,
27394     deferHeight: true,
27395     initialized : false,
27396     activated : false,
27397     
27398     onFocus : Roo.emptyFn,
27399     iframePad:3,
27400     hideMode:'offsets',
27401     
27402     tbContainer : false,
27403     
27404     bodyCls : '',
27405     
27406     toolbarContainer :function() {
27407         return this.wrap.select('.x-html-editor-tb',true).first();
27408     },
27409
27410     /**
27411      * Protected method that will not generally be called directly. It
27412      * is called when the editor creates its toolbar. Override this method if you need to
27413      * add custom toolbar buttons.
27414      * @param {HtmlEditor} editor
27415      */
27416     createToolbar : function(){
27417         Roo.log('renewing');
27418         Roo.log("create toolbars");
27419         
27420         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27421         this.toolbars[0].render(this.toolbarContainer());
27422         
27423         return;
27424         
27425 //        if (!editor.toolbars || !editor.toolbars.length) {
27426 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27427 //        }
27428 //        
27429 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27430 //            editor.toolbars[i] = Roo.factory(
27431 //                    typeof(editor.toolbars[i]) == 'string' ?
27432 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27433 //                Roo.bootstrap.HtmlEditor);
27434 //            editor.toolbars[i].init(editor);
27435 //        }
27436     },
27437
27438      
27439     // private
27440     onRender : function(ct, position)
27441     {
27442        // Roo.log("Call onRender: " + this.xtype);
27443         var _t = this;
27444         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27445       
27446         this.wrap = this.inputEl().wrap({
27447             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27448         });
27449         
27450         this.editorcore.onRender(ct, position);
27451          
27452         if (this.resizable) {
27453             this.resizeEl = new Roo.Resizable(this.wrap, {
27454                 pinned : true,
27455                 wrap: true,
27456                 dynamic : true,
27457                 minHeight : this.height,
27458                 height: this.height,
27459                 handles : this.resizable,
27460                 width: this.width,
27461                 listeners : {
27462                     resize : function(r, w, h) {
27463                         _t.onResize(w,h); // -something
27464                     }
27465                 }
27466             });
27467             
27468         }
27469         this.createToolbar(this);
27470        
27471         
27472         if(!this.width && this.resizable){
27473             this.setSize(this.wrap.getSize());
27474         }
27475         if (this.resizeEl) {
27476             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27477             // should trigger onReize..
27478         }
27479         
27480     },
27481
27482     // private
27483     onResize : function(w, h)
27484     {
27485         Roo.log('resize: ' +w + ',' + h );
27486         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27487         var ew = false;
27488         var eh = false;
27489         
27490         if(this.inputEl() ){
27491             if(typeof w == 'number'){
27492                 var aw = w - this.wrap.getFrameWidth('lr');
27493                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27494                 ew = aw;
27495             }
27496             if(typeof h == 'number'){
27497                  var tbh = -11;  // fixme it needs to tool bar size!
27498                 for (var i =0; i < this.toolbars.length;i++) {
27499                     // fixme - ask toolbars for heights?
27500                     tbh += this.toolbars[i].el.getHeight();
27501                     //if (this.toolbars[i].footer) {
27502                     //    tbh += this.toolbars[i].footer.el.getHeight();
27503                     //}
27504                 }
27505               
27506                 
27507                 
27508                 
27509                 
27510                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27511                 ah -= 5; // knock a few pixes off for look..
27512                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27513                 var eh = ah;
27514             }
27515         }
27516         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27517         this.editorcore.onResize(ew,eh);
27518         
27519     },
27520
27521     /**
27522      * Toggles the editor between standard and source edit mode.
27523      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27524      */
27525     toggleSourceEdit : function(sourceEditMode)
27526     {
27527         this.editorcore.toggleSourceEdit(sourceEditMode);
27528         
27529         if(this.editorcore.sourceEditMode){
27530             Roo.log('editor - showing textarea');
27531             
27532 //            Roo.log('in');
27533 //            Roo.log(this.syncValue());
27534             this.syncValue();
27535             this.inputEl().removeClass(['hide', 'x-hidden']);
27536             this.inputEl().dom.removeAttribute('tabIndex');
27537             this.inputEl().focus();
27538         }else{
27539             Roo.log('editor - hiding textarea');
27540 //            Roo.log('out')
27541 //            Roo.log(this.pushValue()); 
27542             this.pushValue();
27543             
27544             this.inputEl().addClass(['hide', 'x-hidden']);
27545             this.inputEl().dom.setAttribute('tabIndex', -1);
27546             //this.deferFocus();
27547         }
27548          
27549         if(this.resizable){
27550             this.setSize(this.wrap.getSize());
27551         }
27552         
27553         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27554     },
27555  
27556     // private (for BoxComponent)
27557     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27558
27559     // private (for BoxComponent)
27560     getResizeEl : function(){
27561         return this.wrap;
27562     },
27563
27564     // private (for BoxComponent)
27565     getPositionEl : function(){
27566         return this.wrap;
27567     },
27568
27569     // private
27570     initEvents : function(){
27571         this.originalValue = this.getValue();
27572     },
27573
27574 //    /**
27575 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27576 //     * @method
27577 //     */
27578 //    markInvalid : Roo.emptyFn,
27579 //    /**
27580 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27581 //     * @method
27582 //     */
27583 //    clearInvalid : Roo.emptyFn,
27584
27585     setValue : function(v){
27586         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27587         this.editorcore.pushValue();
27588     },
27589
27590      
27591     // private
27592     deferFocus : function(){
27593         this.focus.defer(10, this);
27594     },
27595
27596     // doc'ed in Field
27597     focus : function(){
27598         this.editorcore.focus();
27599         
27600     },
27601       
27602
27603     // private
27604     onDestroy : function(){
27605         
27606         
27607         
27608         if(this.rendered){
27609             
27610             for (var i =0; i < this.toolbars.length;i++) {
27611                 // fixme - ask toolbars for heights?
27612                 this.toolbars[i].onDestroy();
27613             }
27614             
27615             this.wrap.dom.innerHTML = '';
27616             this.wrap.remove();
27617         }
27618     },
27619
27620     // private
27621     onFirstFocus : function(){
27622         //Roo.log("onFirstFocus");
27623         this.editorcore.onFirstFocus();
27624          for (var i =0; i < this.toolbars.length;i++) {
27625             this.toolbars[i].onFirstFocus();
27626         }
27627         
27628     },
27629     
27630     // private
27631     syncValue : function()
27632     {   
27633         this.editorcore.syncValue();
27634     },
27635     
27636     pushValue : function()
27637     {   
27638         this.editorcore.pushValue();
27639     }
27640      
27641     
27642     // hide stuff that is not compatible
27643     /**
27644      * @event blur
27645      * @hide
27646      */
27647     /**
27648      * @event change
27649      * @hide
27650      */
27651     /**
27652      * @event focus
27653      * @hide
27654      */
27655     /**
27656      * @event specialkey
27657      * @hide
27658      */
27659     /**
27660      * @cfg {String} fieldClass @hide
27661      */
27662     /**
27663      * @cfg {String} focusClass @hide
27664      */
27665     /**
27666      * @cfg {String} autoCreate @hide
27667      */
27668     /**
27669      * @cfg {String} inputType @hide
27670      */
27671      
27672     /**
27673      * @cfg {String} invalidText @hide
27674      */
27675     /**
27676      * @cfg {String} msgFx @hide
27677      */
27678     /**
27679      * @cfg {String} validateOnBlur @hide
27680      */
27681 });
27682  
27683     
27684    
27685    
27686    
27687       
27688 Roo.namespace('Roo.bootstrap.htmleditor');
27689 /**
27690  * @class Roo.bootstrap.HtmlEditorToolbar1
27691  * Basic Toolbar
27692  * 
27693  * @example
27694  * Usage:
27695  *
27696  new Roo.bootstrap.HtmlEditor({
27697     ....
27698     toolbars : [
27699         new Roo.bootstrap.HtmlEditorToolbar1({
27700             disable : { fonts: 1 , format: 1, ..., ... , ...],
27701             btns : [ .... ]
27702         })
27703     }
27704      
27705  * 
27706  * @cfg {Object} disable List of elements to disable..
27707  * @cfg {Array} btns List of additional buttons.
27708  * 
27709  * 
27710  * NEEDS Extra CSS? 
27711  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27712  */
27713  
27714 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27715 {
27716     
27717     Roo.apply(this, config);
27718     
27719     // default disabled, based on 'good practice'..
27720     this.disable = this.disable || {};
27721     Roo.applyIf(this.disable, {
27722         fontSize : true,
27723         colors : true,
27724         specialElements : true
27725     });
27726     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27727     
27728     this.editor = config.editor;
27729     this.editorcore = config.editor.editorcore;
27730     
27731     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27732     
27733     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27734     // dont call parent... till later.
27735 }
27736 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27737      
27738     bar : true,
27739     
27740     editor : false,
27741     editorcore : false,
27742     
27743     
27744     formats : [
27745         "p" ,  
27746         "h1","h2","h3","h4","h5","h6", 
27747         "pre", "code", 
27748         "abbr", "acronym", "address", "cite", "samp", "var",
27749         'div','span'
27750     ],
27751     
27752     onRender : function(ct, position)
27753     {
27754        // Roo.log("Call onRender: " + this.xtype);
27755         
27756        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27757        Roo.log(this.el);
27758        this.el.dom.style.marginBottom = '0';
27759        var _this = this;
27760        var editorcore = this.editorcore;
27761        var editor= this.editor;
27762        
27763        var children = [];
27764        var btn = function(id,cmd , toggle, handler, html){
27765        
27766             var  event = toggle ? 'toggle' : 'click';
27767        
27768             var a = {
27769                 size : 'sm',
27770                 xtype: 'Button',
27771                 xns: Roo.bootstrap,
27772                 //glyphicon : id,
27773                 fa: id,
27774                 cmd : id || cmd,
27775                 enableToggle:toggle !== false,
27776                 html : html || '',
27777                 pressed : toggle ? false : null,
27778                 listeners : {}
27779             };
27780             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27781                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27782             };
27783             children.push(a);
27784             return a;
27785        }
27786        
27787     //    var cb_box = function...
27788         
27789         var style = {
27790                 xtype: 'Button',
27791                 size : 'sm',
27792                 xns: Roo.bootstrap,
27793                 fa : 'font',
27794                 //html : 'submit'
27795                 menu : {
27796                     xtype: 'Menu',
27797                     xns: Roo.bootstrap,
27798                     items:  []
27799                 }
27800         };
27801         Roo.each(this.formats, function(f) {
27802             style.menu.items.push({
27803                 xtype :'MenuItem',
27804                 xns: Roo.bootstrap,
27805                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27806                 tagname : f,
27807                 listeners : {
27808                     click : function()
27809                     {
27810                         editorcore.insertTag(this.tagname);
27811                         editor.focus();
27812                     }
27813                 }
27814                 
27815             });
27816         });
27817         children.push(style);   
27818         
27819         btn('bold',false,true);
27820         btn('italic',false,true);
27821         btn('align-left', 'justifyleft',true);
27822         btn('align-center', 'justifycenter',true);
27823         btn('align-right' , 'justifyright',true);
27824         btn('link', false, false, function(btn) {
27825             //Roo.log("create link?");
27826             var url = prompt(this.createLinkText, this.defaultLinkValue);
27827             if(url && url != 'http:/'+'/'){
27828                 this.editorcore.relayCmd('createlink', url);
27829             }
27830         }),
27831         btn('list','insertunorderedlist',true);
27832         btn('pencil', false,true, function(btn){
27833                 Roo.log(this);
27834                 this.toggleSourceEdit(btn.pressed);
27835         });
27836         
27837         if (this.editor.btns.length > 0) {
27838             for (var i = 0; i<this.editor.btns.length; i++) {
27839                 children.push(this.editor.btns[i]);
27840             }
27841         }
27842         
27843         /*
27844         var cog = {
27845                 xtype: 'Button',
27846                 size : 'sm',
27847                 xns: Roo.bootstrap,
27848                 glyphicon : 'cog',
27849                 //html : 'submit'
27850                 menu : {
27851                     xtype: 'Menu',
27852                     xns: Roo.bootstrap,
27853                     items:  []
27854                 }
27855         };
27856         
27857         cog.menu.items.push({
27858             xtype :'MenuItem',
27859             xns: Roo.bootstrap,
27860             html : Clean styles,
27861             tagname : f,
27862             listeners : {
27863                 click : function()
27864                 {
27865                     editorcore.insertTag(this.tagname);
27866                     editor.focus();
27867                 }
27868             }
27869             
27870         });
27871        */
27872         
27873          
27874        this.xtype = 'NavSimplebar';
27875         
27876         for(var i=0;i< children.length;i++) {
27877             
27878             this.buttons.add(this.addxtypeChild(children[i]));
27879             
27880         }
27881         
27882         editor.on('editorevent', this.updateToolbar, this);
27883     },
27884     onBtnClick : function(id)
27885     {
27886        this.editorcore.relayCmd(id);
27887        this.editorcore.focus();
27888     },
27889     
27890     /**
27891      * Protected method that will not generally be called directly. It triggers
27892      * a toolbar update by reading the markup state of the current selection in the editor.
27893      */
27894     updateToolbar: function(){
27895
27896         if(!this.editorcore.activated){
27897             this.editor.onFirstFocus(); // is this neeed?
27898             return;
27899         }
27900
27901         var btns = this.buttons; 
27902         var doc = this.editorcore.doc;
27903         btns.get('bold').setActive(doc.queryCommandState('bold'));
27904         btns.get('italic').setActive(doc.queryCommandState('italic'));
27905         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27906         
27907         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27908         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27909         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27910         
27911         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27912         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27913          /*
27914         
27915         var ans = this.editorcore.getAllAncestors();
27916         if (this.formatCombo) {
27917             
27918             
27919             var store = this.formatCombo.store;
27920             this.formatCombo.setValue("");
27921             for (var i =0; i < ans.length;i++) {
27922                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27923                     // select it..
27924                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27925                     break;
27926                 }
27927             }
27928         }
27929         
27930         
27931         
27932         // hides menus... - so this cant be on a menu...
27933         Roo.bootstrap.MenuMgr.hideAll();
27934         */
27935         Roo.bootstrap.MenuMgr.hideAll();
27936         //this.editorsyncValue();
27937     },
27938     onFirstFocus: function() {
27939         this.buttons.each(function(item){
27940            item.enable();
27941         });
27942     },
27943     toggleSourceEdit : function(sourceEditMode){
27944         
27945           
27946         if(sourceEditMode){
27947             Roo.log("disabling buttons");
27948            this.buttons.each( function(item){
27949                 if(item.cmd != 'pencil'){
27950                     item.disable();
27951                 }
27952             });
27953           
27954         }else{
27955             Roo.log("enabling buttons");
27956             if(this.editorcore.initialized){
27957                 this.buttons.each( function(item){
27958                     item.enable();
27959                 });
27960             }
27961             
27962         }
27963         Roo.log("calling toggole on editor");
27964         // tell the editor that it's been pressed..
27965         this.editor.toggleSourceEdit(sourceEditMode);
27966        
27967     }
27968 });
27969
27970
27971
27972
27973  
27974 /*
27975  * - LGPL
27976  */
27977
27978 /**
27979  * @class Roo.bootstrap.Markdown
27980  * @extends Roo.bootstrap.TextArea
27981  * Bootstrap Showdown editable area
27982  * @cfg {string} content
27983  * 
27984  * @constructor
27985  * Create a new Showdown
27986  */
27987
27988 Roo.bootstrap.Markdown = function(config){
27989     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27990    
27991 };
27992
27993 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27994     
27995     editing :false,
27996     
27997     initEvents : function()
27998     {
27999         
28000         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28001         this.markdownEl = this.el.createChild({
28002             cls : 'roo-markdown-area'
28003         });
28004         this.inputEl().addClass('d-none');
28005         if (this.getValue() == '') {
28006             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28007             
28008         } else {
28009             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28010         }
28011         this.markdownEl.on('click', this.toggleTextEdit, this);
28012         this.on('blur', this.toggleTextEdit, this);
28013         this.on('specialkey', this.resizeTextArea, this);
28014     },
28015     
28016     toggleTextEdit : function()
28017     {
28018         var sh = this.markdownEl.getHeight();
28019         this.inputEl().addClass('d-none');
28020         this.markdownEl.addClass('d-none');
28021         if (!this.editing) {
28022             // show editor?
28023             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28024             this.inputEl().removeClass('d-none');
28025             this.inputEl().focus();
28026             this.editing = true;
28027             return;
28028         }
28029         // show showdown...
28030         this.updateMarkdown();
28031         this.markdownEl.removeClass('d-none');
28032         this.editing = false;
28033         return;
28034     },
28035     updateMarkdown : function()
28036     {
28037         if (this.getValue() == '') {
28038             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28039             return;
28040         }
28041  
28042         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28043     },
28044     
28045     resizeTextArea: function () {
28046         
28047         var sh = 100;
28048         Roo.log([sh, this.getValue().split("\n").length * 30]);
28049         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28050     },
28051     setValue : function(val)
28052     {
28053         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28054         if (!this.editing) {
28055             this.updateMarkdown();
28056         }
28057         
28058     },
28059     focus : function()
28060     {
28061         if (!this.editing) {
28062             this.toggleTextEdit();
28063         }
28064         
28065     }
28066
28067
28068 });/*
28069  * Based on:
28070  * Ext JS Library 1.1.1
28071  * Copyright(c) 2006-2007, Ext JS, LLC.
28072  *
28073  * Originally Released Under LGPL - original licence link has changed is not relivant.
28074  *
28075  * Fork - LGPL
28076  * <script type="text/javascript">
28077  */
28078  
28079 /**
28080  * @class Roo.bootstrap.PagingToolbar
28081  * @extends Roo.bootstrap.NavSimplebar
28082  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28083  * @constructor
28084  * Create a new PagingToolbar
28085  * @param {Object} config The config object
28086  * @param {Roo.data.Store} store
28087  */
28088 Roo.bootstrap.PagingToolbar = function(config)
28089 {
28090     // old args format still supported... - xtype is prefered..
28091         // created from xtype...
28092     
28093     this.ds = config.dataSource;
28094     
28095     if (config.store && !this.ds) {
28096         this.store= Roo.factory(config.store, Roo.data);
28097         this.ds = this.store;
28098         this.ds.xmodule = this.xmodule || false;
28099     }
28100     
28101     this.toolbarItems = [];
28102     if (config.items) {
28103         this.toolbarItems = config.items;
28104     }
28105     
28106     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28107     
28108     this.cursor = 0;
28109     
28110     if (this.ds) { 
28111         this.bind(this.ds);
28112     }
28113     
28114     if (Roo.bootstrap.version == 4) {
28115         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28116     } else {
28117         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28118     }
28119     
28120 };
28121
28122 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28123     /**
28124      * @cfg {Roo.data.Store} dataSource
28125      * The underlying data store providing the paged data
28126      */
28127     /**
28128      * @cfg {String/HTMLElement/Element} container
28129      * container The id or element that will contain the toolbar
28130      */
28131     /**
28132      * @cfg {Boolean} displayInfo
28133      * True to display the displayMsg (defaults to false)
28134      */
28135     /**
28136      * @cfg {Number} pageSize
28137      * The number of records to display per page (defaults to 20)
28138      */
28139     pageSize: 20,
28140     /**
28141      * @cfg {String} displayMsg
28142      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28143      */
28144     displayMsg : 'Displaying {0} - {1} of {2}',
28145     /**
28146      * @cfg {String} emptyMsg
28147      * The message to display when no records are found (defaults to "No data to display")
28148      */
28149     emptyMsg : 'No data to display',
28150     /**
28151      * Customizable piece of the default paging text (defaults to "Page")
28152      * @type String
28153      */
28154     beforePageText : "Page",
28155     /**
28156      * Customizable piece of the default paging text (defaults to "of %0")
28157      * @type String
28158      */
28159     afterPageText : "of {0}",
28160     /**
28161      * Customizable piece of the default paging text (defaults to "First Page")
28162      * @type String
28163      */
28164     firstText : "First Page",
28165     /**
28166      * Customizable piece of the default paging text (defaults to "Previous Page")
28167      * @type String
28168      */
28169     prevText : "Previous Page",
28170     /**
28171      * Customizable piece of the default paging text (defaults to "Next Page")
28172      * @type String
28173      */
28174     nextText : "Next Page",
28175     /**
28176      * Customizable piece of the default paging text (defaults to "Last Page")
28177      * @type String
28178      */
28179     lastText : "Last Page",
28180     /**
28181      * Customizable piece of the default paging text (defaults to "Refresh")
28182      * @type String
28183      */
28184     refreshText : "Refresh",
28185
28186     buttons : false,
28187     // private
28188     onRender : function(ct, position) 
28189     {
28190         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28191         this.navgroup.parentId = this.id;
28192         this.navgroup.onRender(this.el, null);
28193         // add the buttons to the navgroup
28194         
28195         if(this.displayInfo){
28196             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28197             this.displayEl = this.el.select('.x-paging-info', true).first();
28198 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28199 //            this.displayEl = navel.el.select('span',true).first();
28200         }
28201         
28202         var _this = this;
28203         
28204         if(this.buttons){
28205             Roo.each(_this.buttons, function(e){ // this might need to use render????
28206                Roo.factory(e).render(_this.el);
28207             });
28208         }
28209             
28210         Roo.each(_this.toolbarItems, function(e) {
28211             _this.navgroup.addItem(e);
28212         });
28213         
28214         
28215         this.first = this.navgroup.addItem({
28216             tooltip: this.firstText,
28217             cls: "prev btn-outline-secondary",
28218             html : ' <i class="fa fa-step-backward"></i>',
28219             disabled: true,
28220             preventDefault: true,
28221             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28222         });
28223         
28224         this.prev =  this.navgroup.addItem({
28225             tooltip: this.prevText,
28226             cls: "prev btn-outline-secondary",
28227             html : ' <i class="fa fa-backward"></i>',
28228             disabled: true,
28229             preventDefault: true,
28230             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28231         });
28232     //this.addSeparator();
28233         
28234         
28235         var field = this.navgroup.addItem( {
28236             tagtype : 'span',
28237             cls : 'x-paging-position  btn-outline-secondary',
28238              disabled: true,
28239             html : this.beforePageText  +
28240                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28241                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28242          } ); //?? escaped?
28243         
28244         this.field = field.el.select('input', true).first();
28245         this.field.on("keydown", this.onPagingKeydown, this);
28246         this.field.on("focus", function(){this.dom.select();});
28247     
28248     
28249         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28250         //this.field.setHeight(18);
28251         //this.addSeparator();
28252         this.next = this.navgroup.addItem({
28253             tooltip: this.nextText,
28254             cls: "next btn-outline-secondary",
28255             html : ' <i class="fa fa-forward"></i>',
28256             disabled: true,
28257             preventDefault: true,
28258             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28259         });
28260         this.last = this.navgroup.addItem({
28261             tooltip: this.lastText,
28262             html : ' <i class="fa fa-step-forward"></i>',
28263             cls: "next btn-outline-secondary",
28264             disabled: true,
28265             preventDefault: true,
28266             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28267         });
28268     //this.addSeparator();
28269         this.loading = this.navgroup.addItem({
28270             tooltip: this.refreshText,
28271             cls: "btn-outline-secondary",
28272             html : ' <i class="fa fa-refresh"></i>',
28273             preventDefault: true,
28274             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28275         });
28276         
28277     },
28278
28279     // private
28280     updateInfo : function(){
28281         if(this.displayEl){
28282             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28283             var msg = count == 0 ?
28284                 this.emptyMsg :
28285                 String.format(
28286                     this.displayMsg,
28287                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28288                 );
28289             this.displayEl.update(msg);
28290         }
28291     },
28292
28293     // private
28294     onLoad : function(ds, r, o)
28295     {
28296         this.cursor = o.params && o.params.start ? o.params.start : 0;
28297         
28298         var d = this.getPageData(),
28299             ap = d.activePage,
28300             ps = d.pages;
28301         
28302         
28303         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28304         this.field.dom.value = ap;
28305         this.first.setDisabled(ap == 1);
28306         this.prev.setDisabled(ap == 1);
28307         this.next.setDisabled(ap == ps);
28308         this.last.setDisabled(ap == ps);
28309         this.loading.enable();
28310         this.updateInfo();
28311     },
28312
28313     // private
28314     getPageData : function(){
28315         var total = this.ds.getTotalCount();
28316         return {
28317             total : total,
28318             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28319             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28320         };
28321     },
28322
28323     // private
28324     onLoadError : function(){
28325         this.loading.enable();
28326     },
28327
28328     // private
28329     onPagingKeydown : function(e){
28330         var k = e.getKey();
28331         var d = this.getPageData();
28332         if(k == e.RETURN){
28333             var v = this.field.dom.value, pageNum;
28334             if(!v || isNaN(pageNum = parseInt(v, 10))){
28335                 this.field.dom.value = d.activePage;
28336                 return;
28337             }
28338             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28339             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28340             e.stopEvent();
28341         }
28342         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))
28343         {
28344           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28345           this.field.dom.value = pageNum;
28346           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28347           e.stopEvent();
28348         }
28349         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28350         {
28351           var v = this.field.dom.value, pageNum; 
28352           var increment = (e.shiftKey) ? 10 : 1;
28353           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28354                 increment *= -1;
28355           }
28356           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28357             this.field.dom.value = d.activePage;
28358             return;
28359           }
28360           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28361           {
28362             this.field.dom.value = parseInt(v, 10) + increment;
28363             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28364             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28365           }
28366           e.stopEvent();
28367         }
28368     },
28369
28370     // private
28371     beforeLoad : function(){
28372         if(this.loading){
28373             this.loading.disable();
28374         }
28375     },
28376
28377     // private
28378     onClick : function(which){
28379         
28380         var ds = this.ds;
28381         if (!ds) {
28382             return;
28383         }
28384         
28385         switch(which){
28386             case "first":
28387                 ds.load({params:{start: 0, limit: this.pageSize}});
28388             break;
28389             case "prev":
28390                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28391             break;
28392             case "next":
28393                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28394             break;
28395             case "last":
28396                 var total = ds.getTotalCount();
28397                 var extra = total % this.pageSize;
28398                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28399                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28400             break;
28401             case "refresh":
28402                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28403             break;
28404         }
28405     },
28406
28407     /**
28408      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28409      * @param {Roo.data.Store} store The data store to unbind
28410      */
28411     unbind : function(ds){
28412         ds.un("beforeload", this.beforeLoad, this);
28413         ds.un("load", this.onLoad, this);
28414         ds.un("loadexception", this.onLoadError, this);
28415         ds.un("remove", this.updateInfo, this);
28416         ds.un("add", this.updateInfo, this);
28417         this.ds = undefined;
28418     },
28419
28420     /**
28421      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28422      * @param {Roo.data.Store} store The data store to bind
28423      */
28424     bind : function(ds){
28425         ds.on("beforeload", this.beforeLoad, this);
28426         ds.on("load", this.onLoad, this);
28427         ds.on("loadexception", this.onLoadError, this);
28428         ds.on("remove", this.updateInfo, this);
28429         ds.on("add", this.updateInfo, this);
28430         this.ds = ds;
28431     }
28432 });/*
28433  * - LGPL
28434  *
28435  * element
28436  * 
28437  */
28438
28439 /**
28440  * @class Roo.bootstrap.MessageBar
28441  * @extends Roo.bootstrap.Component
28442  * Bootstrap MessageBar class
28443  * @cfg {String} html contents of the MessageBar
28444  * @cfg {String} weight (info | success | warning | danger) default info
28445  * @cfg {String} beforeClass insert the bar before the given class
28446  * @cfg {Boolean} closable (true | false) default false
28447  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28448  * 
28449  * @constructor
28450  * Create a new Element
28451  * @param {Object} config The config object
28452  */
28453
28454 Roo.bootstrap.MessageBar = function(config){
28455     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28456 };
28457
28458 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28459     
28460     html: '',
28461     weight: 'info',
28462     closable: false,
28463     fixed: false,
28464     beforeClass: 'bootstrap-sticky-wrap',
28465     
28466     getAutoCreate : function(){
28467         
28468         var cfg = {
28469             tag: 'div',
28470             cls: 'alert alert-dismissable alert-' + this.weight,
28471             cn: [
28472                 {
28473                     tag: 'span',
28474                     cls: 'message',
28475                     html: this.html || ''
28476                 }
28477             ]
28478         };
28479         
28480         if(this.fixed){
28481             cfg.cls += ' alert-messages-fixed';
28482         }
28483         
28484         if(this.closable){
28485             cfg.cn.push({
28486                 tag: 'button',
28487                 cls: 'close',
28488                 html: 'x'
28489             });
28490         }
28491         
28492         return cfg;
28493     },
28494     
28495     onRender : function(ct, position)
28496     {
28497         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28498         
28499         if(!this.el){
28500             var cfg = Roo.apply({},  this.getAutoCreate());
28501             cfg.id = Roo.id();
28502             
28503             if (this.cls) {
28504                 cfg.cls += ' ' + this.cls;
28505             }
28506             if (this.style) {
28507                 cfg.style = this.style;
28508             }
28509             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28510             
28511             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28512         }
28513         
28514         this.el.select('>button.close').on('click', this.hide, this);
28515         
28516     },
28517     
28518     show : function()
28519     {
28520         if (!this.rendered) {
28521             this.render();
28522         }
28523         
28524         this.el.show();
28525         
28526         this.fireEvent('show', this);
28527         
28528     },
28529     
28530     hide : function()
28531     {
28532         if (!this.rendered) {
28533             this.render();
28534         }
28535         
28536         this.el.hide();
28537         
28538         this.fireEvent('hide', this);
28539     },
28540     
28541     update : function()
28542     {
28543 //        var e = this.el.dom.firstChild;
28544 //        
28545 //        if(this.closable){
28546 //            e = e.nextSibling;
28547 //        }
28548 //        
28549 //        e.data = this.html || '';
28550
28551         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28552     }
28553    
28554 });
28555
28556  
28557
28558      /*
28559  * - LGPL
28560  *
28561  * Graph
28562  * 
28563  */
28564
28565
28566 /**
28567  * @class Roo.bootstrap.Graph
28568  * @extends Roo.bootstrap.Component
28569  * Bootstrap Graph class
28570 > Prameters
28571  -sm {number} sm 4
28572  -md {number} md 5
28573  @cfg {String} graphtype  bar | vbar | pie
28574  @cfg {number} g_x coodinator | centre x (pie)
28575  @cfg {number} g_y coodinator | centre y (pie)
28576  @cfg {number} g_r radius (pie)
28577  @cfg {number} g_height height of the chart (respected by all elements in the set)
28578  @cfg {number} g_width width of the chart (respected by all elements in the set)
28579  @cfg {Object} title The title of the chart
28580     
28581  -{Array}  values
28582  -opts (object) options for the chart 
28583      o {
28584      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28585      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28586      o vgutter (number)
28587      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.
28588      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28589      o to
28590      o stretch (boolean)
28591      o }
28592  -opts (object) options for the pie
28593      o{
28594      o cut
28595      o startAngle (number)
28596      o endAngle (number)
28597      } 
28598  *
28599  * @constructor
28600  * Create a new Input
28601  * @param {Object} config The config object
28602  */
28603
28604 Roo.bootstrap.Graph = function(config){
28605     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28606     
28607     this.addEvents({
28608         // img events
28609         /**
28610          * @event click
28611          * The img click event for the img.
28612          * @param {Roo.EventObject} e
28613          */
28614         "click" : true
28615     });
28616 };
28617
28618 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28619     
28620     sm: 4,
28621     md: 5,
28622     graphtype: 'bar',
28623     g_height: 250,
28624     g_width: 400,
28625     g_x: 50,
28626     g_y: 50,
28627     g_r: 30,
28628     opts:{
28629         //g_colors: this.colors,
28630         g_type: 'soft',
28631         g_gutter: '20%'
28632
28633     },
28634     title : false,
28635
28636     getAutoCreate : function(){
28637         
28638         var cfg = {
28639             tag: 'div',
28640             html : null
28641         };
28642         
28643         
28644         return  cfg;
28645     },
28646
28647     onRender : function(ct,position){
28648         
28649         
28650         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28651         
28652         if (typeof(Raphael) == 'undefined') {
28653             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28654             return;
28655         }
28656         
28657         this.raphael = Raphael(this.el.dom);
28658         
28659                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28660                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28661                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28662                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28663                 /*
28664                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28665                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28666                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28667                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28668                 
28669                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28670                 r.barchart(330, 10, 300, 220, data1);
28671                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28672                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28673                 */
28674                 
28675                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28676                 // r.barchart(30, 30, 560, 250,  xdata, {
28677                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28678                 //     axis : "0 0 1 1",
28679                 //     axisxlabels :  xdata
28680                 //     //yvalues : cols,
28681                    
28682                 // });
28683 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28684 //        
28685 //        this.load(null,xdata,{
28686 //                axis : "0 0 1 1",
28687 //                axisxlabels :  xdata
28688 //                });
28689
28690     },
28691
28692     load : function(graphtype,xdata,opts)
28693     {
28694         this.raphael.clear();
28695         if(!graphtype) {
28696             graphtype = this.graphtype;
28697         }
28698         if(!opts){
28699             opts = this.opts;
28700         }
28701         var r = this.raphael,
28702             fin = function () {
28703                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28704             },
28705             fout = function () {
28706                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28707             },
28708             pfin = function() {
28709                 this.sector.stop();
28710                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28711
28712                 if (this.label) {
28713                     this.label[0].stop();
28714                     this.label[0].attr({ r: 7.5 });
28715                     this.label[1].attr({ "font-weight": 800 });
28716                 }
28717             },
28718             pfout = function() {
28719                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28720
28721                 if (this.label) {
28722                     this.label[0].animate({ r: 5 }, 500, "bounce");
28723                     this.label[1].attr({ "font-weight": 400 });
28724                 }
28725             };
28726
28727         switch(graphtype){
28728             case 'bar':
28729                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28730                 break;
28731             case 'hbar':
28732                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28733                 break;
28734             case 'pie':
28735 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28736 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28737 //            
28738                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28739                 
28740                 break;
28741
28742         }
28743         
28744         if(this.title){
28745             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28746         }
28747         
28748     },
28749     
28750     setTitle: function(o)
28751     {
28752         this.title = o;
28753     },
28754     
28755     initEvents: function() {
28756         
28757         if(!this.href){
28758             this.el.on('click', this.onClick, this);
28759         }
28760     },
28761     
28762     onClick : function(e)
28763     {
28764         Roo.log('img onclick');
28765         this.fireEvent('click', this, e);
28766     }
28767    
28768 });
28769
28770  
28771 /*
28772  * - LGPL
28773  *
28774  * numberBox
28775  * 
28776  */
28777 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28778
28779 /**
28780  * @class Roo.bootstrap.dash.NumberBox
28781  * @extends Roo.bootstrap.Component
28782  * Bootstrap NumberBox class
28783  * @cfg {String} headline Box headline
28784  * @cfg {String} content Box content
28785  * @cfg {String} icon Box icon
28786  * @cfg {String} footer Footer text
28787  * @cfg {String} fhref Footer href
28788  * 
28789  * @constructor
28790  * Create a new NumberBox
28791  * @param {Object} config The config object
28792  */
28793
28794
28795 Roo.bootstrap.dash.NumberBox = function(config){
28796     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28797     
28798 };
28799
28800 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28801     
28802     headline : '',
28803     content : '',
28804     icon : '',
28805     footer : '',
28806     fhref : '',
28807     ficon : '',
28808     
28809     getAutoCreate : function(){
28810         
28811         var cfg = {
28812             tag : 'div',
28813             cls : 'small-box ',
28814             cn : [
28815                 {
28816                     tag : 'div',
28817                     cls : 'inner',
28818                     cn :[
28819                         {
28820                             tag : 'h3',
28821                             cls : 'roo-headline',
28822                             html : this.headline
28823                         },
28824                         {
28825                             tag : 'p',
28826                             cls : 'roo-content',
28827                             html : this.content
28828                         }
28829                     ]
28830                 }
28831             ]
28832         };
28833         
28834         if(this.icon){
28835             cfg.cn.push({
28836                 tag : 'div',
28837                 cls : 'icon',
28838                 cn :[
28839                     {
28840                         tag : 'i',
28841                         cls : 'ion ' + this.icon
28842                     }
28843                 ]
28844             });
28845         }
28846         
28847         if(this.footer){
28848             var footer = {
28849                 tag : 'a',
28850                 cls : 'small-box-footer',
28851                 href : this.fhref || '#',
28852                 html : this.footer
28853             };
28854             
28855             cfg.cn.push(footer);
28856             
28857         }
28858         
28859         return  cfg;
28860     },
28861
28862     onRender : function(ct,position){
28863         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28864
28865
28866        
28867                 
28868     },
28869
28870     setHeadline: function (value)
28871     {
28872         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28873     },
28874     
28875     setFooter: function (value, href)
28876     {
28877         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28878         
28879         if(href){
28880             this.el.select('a.small-box-footer',true).first().attr('href', href);
28881         }
28882         
28883     },
28884
28885     setContent: function (value)
28886     {
28887         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28888     },
28889
28890     initEvents: function() 
28891     {   
28892         
28893     }
28894     
28895 });
28896
28897  
28898 /*
28899  * - LGPL
28900  *
28901  * TabBox
28902  * 
28903  */
28904 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28905
28906 /**
28907  * @class Roo.bootstrap.dash.TabBox
28908  * @extends Roo.bootstrap.Component
28909  * Bootstrap TabBox class
28910  * @cfg {String} title Title of the TabBox
28911  * @cfg {String} icon Icon of the TabBox
28912  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28913  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28914  * 
28915  * @constructor
28916  * Create a new TabBox
28917  * @param {Object} config The config object
28918  */
28919
28920
28921 Roo.bootstrap.dash.TabBox = function(config){
28922     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28923     this.addEvents({
28924         // raw events
28925         /**
28926          * @event addpane
28927          * When a pane is added
28928          * @param {Roo.bootstrap.dash.TabPane} pane
28929          */
28930         "addpane" : true,
28931         /**
28932          * @event activatepane
28933          * When a pane is activated
28934          * @param {Roo.bootstrap.dash.TabPane} pane
28935          */
28936         "activatepane" : true
28937         
28938          
28939     });
28940     
28941     this.panes = [];
28942 };
28943
28944 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28945
28946     title : '',
28947     icon : false,
28948     showtabs : true,
28949     tabScrollable : false,
28950     
28951     getChildContainer : function()
28952     {
28953         return this.el.select('.tab-content', true).first();
28954     },
28955     
28956     getAutoCreate : function(){
28957         
28958         var header = {
28959             tag: 'li',
28960             cls: 'pull-left header',
28961             html: this.title,
28962             cn : []
28963         };
28964         
28965         if(this.icon){
28966             header.cn.push({
28967                 tag: 'i',
28968                 cls: 'fa ' + this.icon
28969             });
28970         }
28971         
28972         var h = {
28973             tag: 'ul',
28974             cls: 'nav nav-tabs pull-right',
28975             cn: [
28976                 header
28977             ]
28978         };
28979         
28980         if(this.tabScrollable){
28981             h = {
28982                 tag: 'div',
28983                 cls: 'tab-header',
28984                 cn: [
28985                     {
28986                         tag: 'ul',
28987                         cls: 'nav nav-tabs pull-right',
28988                         cn: [
28989                             header
28990                         ]
28991                     }
28992                 ]
28993             };
28994         }
28995         
28996         var cfg = {
28997             tag: 'div',
28998             cls: 'nav-tabs-custom',
28999             cn: [
29000                 h,
29001                 {
29002                     tag: 'div',
29003                     cls: 'tab-content no-padding',
29004                     cn: []
29005                 }
29006             ]
29007         };
29008
29009         return  cfg;
29010     },
29011     initEvents : function()
29012     {
29013         //Roo.log('add add pane handler');
29014         this.on('addpane', this.onAddPane, this);
29015     },
29016      /**
29017      * Updates the box title
29018      * @param {String} html to set the title to.
29019      */
29020     setTitle : function(value)
29021     {
29022         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29023     },
29024     onAddPane : function(pane)
29025     {
29026         this.panes.push(pane);
29027         //Roo.log('addpane');
29028         //Roo.log(pane);
29029         // tabs are rendere left to right..
29030         if(!this.showtabs){
29031             return;
29032         }
29033         
29034         var ctr = this.el.select('.nav-tabs', true).first();
29035          
29036          
29037         var existing = ctr.select('.nav-tab',true);
29038         var qty = existing.getCount();;
29039         
29040         
29041         var tab = ctr.createChild({
29042             tag : 'li',
29043             cls : 'nav-tab' + (qty ? '' : ' active'),
29044             cn : [
29045                 {
29046                     tag : 'a',
29047                     href:'#',
29048                     html : pane.title
29049                 }
29050             ]
29051         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29052         pane.tab = tab;
29053         
29054         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29055         if (!qty) {
29056             pane.el.addClass('active');
29057         }
29058         
29059                 
29060     },
29061     onTabClick : function(ev,un,ob,pane)
29062     {
29063         //Roo.log('tab - prev default');
29064         ev.preventDefault();
29065         
29066         
29067         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29068         pane.tab.addClass('active');
29069         //Roo.log(pane.title);
29070         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29071         // technically we should have a deactivate event.. but maybe add later.
29072         // and it should not de-activate the selected tab...
29073         this.fireEvent('activatepane', pane);
29074         pane.el.addClass('active');
29075         pane.fireEvent('activate');
29076         
29077         
29078     },
29079     
29080     getActivePane : function()
29081     {
29082         var r = false;
29083         Roo.each(this.panes, function(p) {
29084             if(p.el.hasClass('active')){
29085                 r = p;
29086                 return false;
29087             }
29088             
29089             return;
29090         });
29091         
29092         return r;
29093     }
29094     
29095     
29096 });
29097
29098  
29099 /*
29100  * - LGPL
29101  *
29102  * Tab pane
29103  * 
29104  */
29105 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29106 /**
29107  * @class Roo.bootstrap.TabPane
29108  * @extends Roo.bootstrap.Component
29109  * Bootstrap TabPane class
29110  * @cfg {Boolean} active (false | true) Default false
29111  * @cfg {String} title title of panel
29112
29113  * 
29114  * @constructor
29115  * Create a new TabPane
29116  * @param {Object} config The config object
29117  */
29118
29119 Roo.bootstrap.dash.TabPane = function(config){
29120     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29121     
29122     this.addEvents({
29123         // raw events
29124         /**
29125          * @event activate
29126          * When a pane is activated
29127          * @param {Roo.bootstrap.dash.TabPane} pane
29128          */
29129         "activate" : true
29130          
29131     });
29132 };
29133
29134 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29135     
29136     active : false,
29137     title : '',
29138     
29139     // the tabBox that this is attached to.
29140     tab : false,
29141      
29142     getAutoCreate : function() 
29143     {
29144         var cfg = {
29145             tag: 'div',
29146             cls: 'tab-pane'
29147         };
29148         
29149         if(this.active){
29150             cfg.cls += ' active';
29151         }
29152         
29153         return cfg;
29154     },
29155     initEvents  : function()
29156     {
29157         //Roo.log('trigger add pane handler');
29158         this.parent().fireEvent('addpane', this)
29159     },
29160     
29161      /**
29162      * Updates the tab title 
29163      * @param {String} html to set the title to.
29164      */
29165     setTitle: function(str)
29166     {
29167         if (!this.tab) {
29168             return;
29169         }
29170         this.title = str;
29171         this.tab.select('a', true).first().dom.innerHTML = str;
29172         
29173     }
29174     
29175     
29176     
29177 });
29178
29179  
29180
29181
29182  /*
29183  * - LGPL
29184  *
29185  * menu
29186  * 
29187  */
29188 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29189
29190 /**
29191  * @class Roo.bootstrap.menu.Menu
29192  * @extends Roo.bootstrap.Component
29193  * Bootstrap Menu class - container for Menu
29194  * @cfg {String} html Text of the menu
29195  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29196  * @cfg {String} icon Font awesome icon
29197  * @cfg {String} pos Menu align to (top | bottom) default bottom
29198  * 
29199  * 
29200  * @constructor
29201  * Create a new Menu
29202  * @param {Object} config The config object
29203  */
29204
29205
29206 Roo.bootstrap.menu.Menu = function(config){
29207     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29208     
29209     this.addEvents({
29210         /**
29211          * @event beforeshow
29212          * Fires before this menu is displayed
29213          * @param {Roo.bootstrap.menu.Menu} this
29214          */
29215         beforeshow : true,
29216         /**
29217          * @event beforehide
29218          * Fires before this menu is hidden
29219          * @param {Roo.bootstrap.menu.Menu} this
29220          */
29221         beforehide : true,
29222         /**
29223          * @event show
29224          * Fires after this menu is displayed
29225          * @param {Roo.bootstrap.menu.Menu} this
29226          */
29227         show : true,
29228         /**
29229          * @event hide
29230          * Fires after this menu is hidden
29231          * @param {Roo.bootstrap.menu.Menu} this
29232          */
29233         hide : true,
29234         /**
29235          * @event click
29236          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29237          * @param {Roo.bootstrap.menu.Menu} this
29238          * @param {Roo.EventObject} e
29239          */
29240         click : true
29241     });
29242     
29243 };
29244
29245 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29246     
29247     submenu : false,
29248     html : '',
29249     weight : 'default',
29250     icon : false,
29251     pos : 'bottom',
29252     
29253     
29254     getChildContainer : function() {
29255         if(this.isSubMenu){
29256             return this.el;
29257         }
29258         
29259         return this.el.select('ul.dropdown-menu', true).first();  
29260     },
29261     
29262     getAutoCreate : function()
29263     {
29264         var text = [
29265             {
29266                 tag : 'span',
29267                 cls : 'roo-menu-text',
29268                 html : this.html
29269             }
29270         ];
29271         
29272         if(this.icon){
29273             text.unshift({
29274                 tag : 'i',
29275                 cls : 'fa ' + this.icon
29276             })
29277         }
29278         
29279         
29280         var cfg = {
29281             tag : 'div',
29282             cls : 'btn-group',
29283             cn : [
29284                 {
29285                     tag : 'button',
29286                     cls : 'dropdown-button btn btn-' + this.weight,
29287                     cn : text
29288                 },
29289                 {
29290                     tag : 'button',
29291                     cls : 'dropdown-toggle btn btn-' + this.weight,
29292                     cn : [
29293                         {
29294                             tag : 'span',
29295                             cls : 'caret'
29296                         }
29297                     ]
29298                 },
29299                 {
29300                     tag : 'ul',
29301                     cls : 'dropdown-menu'
29302                 }
29303             ]
29304             
29305         };
29306         
29307         if(this.pos == 'top'){
29308             cfg.cls += ' dropup';
29309         }
29310         
29311         if(this.isSubMenu){
29312             cfg = {
29313                 tag : 'ul',
29314                 cls : 'dropdown-menu'
29315             }
29316         }
29317         
29318         return cfg;
29319     },
29320     
29321     onRender : function(ct, position)
29322     {
29323         this.isSubMenu = ct.hasClass('dropdown-submenu');
29324         
29325         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29326     },
29327     
29328     initEvents : function() 
29329     {
29330         if(this.isSubMenu){
29331             return;
29332         }
29333         
29334         this.hidden = true;
29335         
29336         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29337         this.triggerEl.on('click', this.onTriggerPress, this);
29338         
29339         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29340         this.buttonEl.on('click', this.onClick, this);
29341         
29342     },
29343     
29344     list : function()
29345     {
29346         if(this.isSubMenu){
29347             return this.el;
29348         }
29349         
29350         return this.el.select('ul.dropdown-menu', true).first();
29351     },
29352     
29353     onClick : function(e)
29354     {
29355         this.fireEvent("click", this, e);
29356     },
29357     
29358     onTriggerPress  : function(e)
29359     {   
29360         if (this.isVisible()) {
29361             this.hide();
29362         } else {
29363             this.show();
29364         }
29365     },
29366     
29367     isVisible : function(){
29368         return !this.hidden;
29369     },
29370     
29371     show : function()
29372     {
29373         this.fireEvent("beforeshow", this);
29374         
29375         this.hidden = false;
29376         this.el.addClass('open');
29377         
29378         Roo.get(document).on("mouseup", this.onMouseUp, this);
29379         
29380         this.fireEvent("show", this);
29381         
29382         
29383     },
29384     
29385     hide : function()
29386     {
29387         this.fireEvent("beforehide", this);
29388         
29389         this.hidden = true;
29390         this.el.removeClass('open');
29391         
29392         Roo.get(document).un("mouseup", this.onMouseUp);
29393         
29394         this.fireEvent("hide", this);
29395     },
29396     
29397     onMouseUp : function()
29398     {
29399         this.hide();
29400     }
29401     
29402 });
29403
29404  
29405  /*
29406  * - LGPL
29407  *
29408  * menu item
29409  * 
29410  */
29411 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29412
29413 /**
29414  * @class Roo.bootstrap.menu.Item
29415  * @extends Roo.bootstrap.Component
29416  * Bootstrap MenuItem class
29417  * @cfg {Boolean} submenu (true | false) default false
29418  * @cfg {String} html text of the item
29419  * @cfg {String} href the link
29420  * @cfg {Boolean} disable (true | false) default false
29421  * @cfg {Boolean} preventDefault (true | false) default true
29422  * @cfg {String} icon Font awesome icon
29423  * @cfg {String} pos Submenu align to (left | right) default right 
29424  * 
29425  * 
29426  * @constructor
29427  * Create a new Item
29428  * @param {Object} config The config object
29429  */
29430
29431
29432 Roo.bootstrap.menu.Item = function(config){
29433     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29434     this.addEvents({
29435         /**
29436          * @event mouseover
29437          * Fires when the mouse is hovering over this menu
29438          * @param {Roo.bootstrap.menu.Item} this
29439          * @param {Roo.EventObject} e
29440          */
29441         mouseover : true,
29442         /**
29443          * @event mouseout
29444          * Fires when the mouse exits this menu
29445          * @param {Roo.bootstrap.menu.Item} this
29446          * @param {Roo.EventObject} e
29447          */
29448         mouseout : true,
29449         // raw events
29450         /**
29451          * @event click
29452          * The raw click event for the entire grid.
29453          * @param {Roo.EventObject} e
29454          */
29455         click : true
29456     });
29457 };
29458
29459 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29460     
29461     submenu : false,
29462     href : '',
29463     html : '',
29464     preventDefault: true,
29465     disable : false,
29466     icon : false,
29467     pos : 'right',
29468     
29469     getAutoCreate : function()
29470     {
29471         var text = [
29472             {
29473                 tag : 'span',
29474                 cls : 'roo-menu-item-text',
29475                 html : this.html
29476             }
29477         ];
29478         
29479         if(this.icon){
29480             text.unshift({
29481                 tag : 'i',
29482                 cls : 'fa ' + this.icon
29483             })
29484         }
29485         
29486         var cfg = {
29487             tag : 'li',
29488             cn : [
29489                 {
29490                     tag : 'a',
29491                     href : this.href || '#',
29492                     cn : text
29493                 }
29494             ]
29495         };
29496         
29497         if(this.disable){
29498             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29499         }
29500         
29501         if(this.submenu){
29502             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29503             
29504             if(this.pos == 'left'){
29505                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29506             }
29507         }
29508         
29509         return cfg;
29510     },
29511     
29512     initEvents : function() 
29513     {
29514         this.el.on('mouseover', this.onMouseOver, this);
29515         this.el.on('mouseout', this.onMouseOut, this);
29516         
29517         this.el.select('a', true).first().on('click', this.onClick, this);
29518         
29519     },
29520     
29521     onClick : function(e)
29522     {
29523         if(this.preventDefault){
29524             e.preventDefault();
29525         }
29526         
29527         this.fireEvent("click", this, e);
29528     },
29529     
29530     onMouseOver : function(e)
29531     {
29532         if(this.submenu && this.pos == 'left'){
29533             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29534         }
29535         
29536         this.fireEvent("mouseover", this, e);
29537     },
29538     
29539     onMouseOut : function(e)
29540     {
29541         this.fireEvent("mouseout", this, e);
29542     }
29543 });
29544
29545  
29546
29547  /*
29548  * - LGPL
29549  *
29550  * menu separator
29551  * 
29552  */
29553 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29554
29555 /**
29556  * @class Roo.bootstrap.menu.Separator
29557  * @extends Roo.bootstrap.Component
29558  * Bootstrap Separator class
29559  * 
29560  * @constructor
29561  * Create a new Separator
29562  * @param {Object} config The config object
29563  */
29564
29565
29566 Roo.bootstrap.menu.Separator = function(config){
29567     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29568 };
29569
29570 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29571     
29572     getAutoCreate : function(){
29573         var cfg = {
29574             tag : 'li',
29575             cls: 'dropdown-divider divider'
29576         };
29577         
29578         return cfg;
29579     }
29580    
29581 });
29582
29583  
29584
29585  /*
29586  * - LGPL
29587  *
29588  * Tooltip
29589  * 
29590  */
29591
29592 /**
29593  * @class Roo.bootstrap.Tooltip
29594  * Bootstrap Tooltip class
29595  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29596  * to determine which dom element triggers the tooltip.
29597  * 
29598  * It needs to add support for additional attributes like tooltip-position
29599  * 
29600  * @constructor
29601  * Create a new Toolti
29602  * @param {Object} config The config object
29603  */
29604
29605 Roo.bootstrap.Tooltip = function(config){
29606     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29607     
29608     this.alignment = Roo.bootstrap.Tooltip.alignment;
29609     
29610     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29611         this.alignment = config.alignment;
29612     }
29613     
29614 };
29615
29616 Roo.apply(Roo.bootstrap.Tooltip, {
29617     /**
29618      * @function init initialize tooltip monitoring.
29619      * @static
29620      */
29621     currentEl : false,
29622     currentTip : false,
29623     currentRegion : false,
29624     
29625     //  init : delay?
29626     
29627     init : function()
29628     {
29629         Roo.get(document).on('mouseover', this.enter ,this);
29630         Roo.get(document).on('mouseout', this.leave, this);
29631          
29632         
29633         this.currentTip = new Roo.bootstrap.Tooltip();
29634     },
29635     
29636     enter : function(ev)
29637     {
29638         var dom = ev.getTarget();
29639         
29640         //Roo.log(['enter',dom]);
29641         var el = Roo.fly(dom);
29642         if (this.currentEl) {
29643             //Roo.log(dom);
29644             //Roo.log(this.currentEl);
29645             //Roo.log(this.currentEl.contains(dom));
29646             if (this.currentEl == el) {
29647                 return;
29648             }
29649             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29650                 return;
29651             }
29652
29653         }
29654         
29655         if (this.currentTip.el) {
29656             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29657         }    
29658         //Roo.log(ev);
29659         
29660         if(!el || el.dom == document){
29661             return;
29662         }
29663         
29664         var bindEl = el; 
29665         var pel = false;
29666         if (!el.attr('tooltip')) {
29667             pel = el.findParent("[tooltip]");
29668             if (pel) {
29669                 bindEl = Roo.get(pel);
29670             }
29671         }
29672         
29673        
29674         
29675         // you can not look for children, as if el is the body.. then everythign is the child..
29676         if (!pel && !el.attr('tooltip')) { //
29677             if (!el.select("[tooltip]").elements.length) {
29678                 return;
29679             }
29680             // is the mouse over this child...?
29681             bindEl = el.select("[tooltip]").first();
29682             var xy = ev.getXY();
29683             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29684                 //Roo.log("not in region.");
29685                 return;
29686             }
29687             //Roo.log("child element over..");
29688             
29689         }
29690         this.currentEl = el;
29691         this.currentTip.bind(bindEl);
29692         this.currentRegion = Roo.lib.Region.getRegion(dom);
29693         this.currentTip.enter();
29694         
29695     },
29696     leave : function(ev)
29697     {
29698         var dom = ev.getTarget();
29699         //Roo.log(['leave',dom]);
29700         if (!this.currentEl) {
29701             return;
29702         }
29703         
29704         
29705         if (dom != this.currentEl.dom) {
29706             return;
29707         }
29708         var xy = ev.getXY();
29709         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29710             return;
29711         }
29712         // only activate leave if mouse cursor is outside... bounding box..
29713         
29714         
29715         
29716         
29717         if (this.currentTip) {
29718             this.currentTip.leave();
29719         }
29720         //Roo.log('clear currentEl');
29721         this.currentEl = false;
29722         
29723         
29724     },
29725     alignment : {
29726         'left' : ['r-l', [-2,0], 'right'],
29727         'right' : ['l-r', [2,0], 'left'],
29728         'bottom' : ['t-b', [0,2], 'top'],
29729         'top' : [ 'b-t', [0,-2], 'bottom']
29730     }
29731     
29732 });
29733
29734
29735 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29736     
29737     
29738     bindEl : false,
29739     
29740     delay : null, // can be { show : 300 , hide: 500}
29741     
29742     timeout : null,
29743     
29744     hoverState : null, //???
29745     
29746     placement : 'bottom', 
29747     
29748     alignment : false,
29749     
29750     getAutoCreate : function(){
29751     
29752         var cfg = {
29753            cls : 'tooltip',   
29754            role : 'tooltip',
29755            cn : [
29756                 {
29757                     cls : 'tooltip-arrow arrow'
29758                 },
29759                 {
29760                     cls : 'tooltip-inner'
29761                 }
29762            ]
29763         };
29764         
29765         return cfg;
29766     },
29767     bind : function(el)
29768     {
29769         this.bindEl = el;
29770     },
29771     
29772     initEvents : function()
29773     {
29774         this.arrowEl = this.el.select('.arrow', true).first();
29775         this.innerEl = this.el.select('.tooltip-inner', true).first();
29776     },
29777     
29778     enter : function () {
29779        
29780         if (this.timeout != null) {
29781             clearTimeout(this.timeout);
29782         }
29783         
29784         this.hoverState = 'in';
29785          //Roo.log("enter - show");
29786         if (!this.delay || !this.delay.show) {
29787             this.show();
29788             return;
29789         }
29790         var _t = this;
29791         this.timeout = setTimeout(function () {
29792             if (_t.hoverState == 'in') {
29793                 _t.show();
29794             }
29795         }, this.delay.show);
29796     },
29797     leave : function()
29798     {
29799         clearTimeout(this.timeout);
29800     
29801         this.hoverState = 'out';
29802          if (!this.delay || !this.delay.hide) {
29803             this.hide();
29804             return;
29805         }
29806        
29807         var _t = this;
29808         this.timeout = setTimeout(function () {
29809             //Roo.log("leave - timeout");
29810             
29811             if (_t.hoverState == 'out') {
29812                 _t.hide();
29813                 Roo.bootstrap.Tooltip.currentEl = false;
29814             }
29815         }, delay);
29816     },
29817     
29818     show : function (msg)
29819     {
29820         if (!this.el) {
29821             this.render(document.body);
29822         }
29823         // set content.
29824         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29825         
29826         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29827         
29828         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29829         
29830         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29831                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29832         
29833         var placement = typeof this.placement == 'function' ?
29834             this.placement.call(this, this.el, on_el) :
29835             this.placement;
29836             
29837         var autoToken = /\s?auto?\s?/i;
29838         var autoPlace = autoToken.test(placement);
29839         if (autoPlace) {
29840             placement = placement.replace(autoToken, '') || 'top';
29841         }
29842         
29843         //this.el.detach()
29844         //this.el.setXY([0,0]);
29845         this.el.show();
29846         //this.el.dom.style.display='block';
29847         
29848         //this.el.appendTo(on_el);
29849         
29850         var p = this.getPosition();
29851         var box = this.el.getBox();
29852         
29853         if (autoPlace) {
29854             // fixme..
29855         }
29856         
29857         var align = this.alignment[placement];
29858         
29859         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29860         
29861         if(placement == 'top' || placement == 'bottom'){
29862             if(xy[0] < 0){
29863                 placement = 'right';
29864             }
29865             
29866             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29867                 placement = 'left';
29868             }
29869             
29870             var scroll = Roo.select('body', true).first().getScroll();
29871             
29872             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29873                 placement = 'top';
29874             }
29875             
29876             align = this.alignment[placement];
29877             
29878             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29879             
29880         }
29881         
29882         var elems = document.getElementsByTagName('div');
29883         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29884         for (var i = 0; i < elems.length; i++) {
29885           var zindex = Number.parseInt(
29886                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29887                 10
29888           );
29889           if (zindex > highest) {
29890             highest = zindex;
29891           }
29892         }
29893         
29894         
29895         
29896         this.el.dom.style.zIndex = highest;
29897         
29898         this.el.alignTo(this.bindEl, align[0],align[1]);
29899         //var arrow = this.el.select('.arrow',true).first();
29900         //arrow.set(align[2], 
29901         
29902         this.el.addClass(placement);
29903         this.el.addClass("bs-tooltip-"+ placement);
29904         
29905         this.el.addClass('in fade show');
29906         
29907         this.hoverState = null;
29908         
29909         if (this.el.hasClass('fade')) {
29910             // fade it?
29911         }
29912         
29913         
29914         
29915         
29916         
29917     },
29918     hide : function()
29919     {
29920          
29921         if (!this.el) {
29922             return;
29923         }
29924         //this.el.setXY([0,0]);
29925         this.el.removeClass(['show', 'in']);
29926         //this.el.hide();
29927         
29928     }
29929     
29930 });
29931  
29932
29933  /*
29934  * - LGPL
29935  *
29936  * Location Picker
29937  * 
29938  */
29939
29940 /**
29941  * @class Roo.bootstrap.LocationPicker
29942  * @extends Roo.bootstrap.Component
29943  * Bootstrap LocationPicker class
29944  * @cfg {Number} latitude Position when init default 0
29945  * @cfg {Number} longitude Position when init default 0
29946  * @cfg {Number} zoom default 15
29947  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29948  * @cfg {Boolean} mapTypeControl default false
29949  * @cfg {Boolean} disableDoubleClickZoom default false
29950  * @cfg {Boolean} scrollwheel default true
29951  * @cfg {Boolean} streetViewControl default false
29952  * @cfg {Number} radius default 0
29953  * @cfg {String} locationName
29954  * @cfg {Boolean} draggable default true
29955  * @cfg {Boolean} enableAutocomplete default false
29956  * @cfg {Boolean} enableReverseGeocode default true
29957  * @cfg {String} markerTitle
29958  * 
29959  * @constructor
29960  * Create a new LocationPicker
29961  * @param {Object} config The config object
29962  */
29963
29964
29965 Roo.bootstrap.LocationPicker = function(config){
29966     
29967     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29968     
29969     this.addEvents({
29970         /**
29971          * @event initial
29972          * Fires when the picker initialized.
29973          * @param {Roo.bootstrap.LocationPicker} this
29974          * @param {Google Location} location
29975          */
29976         initial : true,
29977         /**
29978          * @event positionchanged
29979          * Fires when the picker position changed.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          * @param {Google Location} location
29982          */
29983         positionchanged : true,
29984         /**
29985          * @event resize
29986          * Fires when the map resize.
29987          * @param {Roo.bootstrap.LocationPicker} this
29988          */
29989         resize : true,
29990         /**
29991          * @event show
29992          * Fires when the map show.
29993          * @param {Roo.bootstrap.LocationPicker} this
29994          */
29995         show : true,
29996         /**
29997          * @event hide
29998          * Fires when the map hide.
29999          * @param {Roo.bootstrap.LocationPicker} this
30000          */
30001         hide : true,
30002         /**
30003          * @event mapClick
30004          * Fires when click the map.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          * @param {Map event} e
30007          */
30008         mapClick : true,
30009         /**
30010          * @event mapRightClick
30011          * Fires when right click the map.
30012          * @param {Roo.bootstrap.LocationPicker} this
30013          * @param {Map event} e
30014          */
30015         mapRightClick : true,
30016         /**
30017          * @event markerClick
30018          * Fires when click the marker.
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          * @param {Map event} e
30021          */
30022         markerClick : true,
30023         /**
30024          * @event markerRightClick
30025          * Fires when right click the marker.
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          * @param {Map event} e
30028          */
30029         markerRightClick : true,
30030         /**
30031          * @event OverlayViewDraw
30032          * Fires when OverlayView Draw
30033          * @param {Roo.bootstrap.LocationPicker} this
30034          */
30035         OverlayViewDraw : true,
30036         /**
30037          * @event OverlayViewOnAdd
30038          * Fires when OverlayView Draw
30039          * @param {Roo.bootstrap.LocationPicker} this
30040          */
30041         OverlayViewOnAdd : true,
30042         /**
30043          * @event OverlayViewOnRemove
30044          * Fires when OverlayView Draw
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          */
30047         OverlayViewOnRemove : true,
30048         /**
30049          * @event OverlayViewShow
30050          * Fires when OverlayView Draw
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          * @param {Pixel} cpx
30053          */
30054         OverlayViewShow : true,
30055         /**
30056          * @event OverlayViewHide
30057          * Fires when OverlayView Draw
30058          * @param {Roo.bootstrap.LocationPicker} this
30059          */
30060         OverlayViewHide : true,
30061         /**
30062          * @event loadexception
30063          * Fires when load google lib failed.
30064          * @param {Roo.bootstrap.LocationPicker} this
30065          */
30066         loadexception : true
30067     });
30068         
30069 };
30070
30071 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30072     
30073     gMapContext: false,
30074     
30075     latitude: 0,
30076     longitude: 0,
30077     zoom: 15,
30078     mapTypeId: false,
30079     mapTypeControl: false,
30080     disableDoubleClickZoom: false,
30081     scrollwheel: true,
30082     streetViewControl: false,
30083     radius: 0,
30084     locationName: '',
30085     draggable: true,
30086     enableAutocomplete: false,
30087     enableReverseGeocode: true,
30088     markerTitle: '',
30089     
30090     getAutoCreate: function()
30091     {
30092
30093         var cfg = {
30094             tag: 'div',
30095             cls: 'roo-location-picker'
30096         };
30097         
30098         return cfg
30099     },
30100     
30101     initEvents: function(ct, position)
30102     {       
30103         if(!this.el.getWidth() || this.isApplied()){
30104             return;
30105         }
30106         
30107         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30108         
30109         this.initial();
30110     },
30111     
30112     initial: function()
30113     {
30114         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30115             this.fireEvent('loadexception', this);
30116             return;
30117         }
30118         
30119         if(!this.mapTypeId){
30120             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30121         }
30122         
30123         this.gMapContext = this.GMapContext();
30124         
30125         this.initOverlayView();
30126         
30127         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30128         
30129         var _this = this;
30130                 
30131         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30132             _this.setPosition(_this.gMapContext.marker.position);
30133         });
30134         
30135         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30136             _this.fireEvent('mapClick', this, event);
30137             
30138         });
30139
30140         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30141             _this.fireEvent('mapRightClick', this, event);
30142             
30143         });
30144         
30145         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30146             _this.fireEvent('markerClick', this, event);
30147             
30148         });
30149
30150         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30151             _this.fireEvent('markerRightClick', this, event);
30152             
30153         });
30154         
30155         this.setPosition(this.gMapContext.location);
30156         
30157         this.fireEvent('initial', this, this.gMapContext.location);
30158     },
30159     
30160     initOverlayView: function()
30161     {
30162         var _this = this;
30163         
30164         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30165             
30166             draw: function()
30167             {
30168                 _this.fireEvent('OverlayViewDraw', _this);
30169             },
30170             
30171             onAdd: function()
30172             {
30173                 _this.fireEvent('OverlayViewOnAdd', _this);
30174             },
30175             
30176             onRemove: function()
30177             {
30178                 _this.fireEvent('OverlayViewOnRemove', _this);
30179             },
30180             
30181             show: function(cpx)
30182             {
30183                 _this.fireEvent('OverlayViewShow', _this, cpx);
30184             },
30185             
30186             hide: function()
30187             {
30188                 _this.fireEvent('OverlayViewHide', _this);
30189             }
30190             
30191         });
30192     },
30193     
30194     fromLatLngToContainerPixel: function(event)
30195     {
30196         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30197     },
30198     
30199     isApplied: function() 
30200     {
30201         return this.getGmapContext() == false ? false : true;
30202     },
30203     
30204     getGmapContext: function() 
30205     {
30206         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30207     },
30208     
30209     GMapContext: function() 
30210     {
30211         var position = new google.maps.LatLng(this.latitude, this.longitude);
30212         
30213         var _map = new google.maps.Map(this.el.dom, {
30214             center: position,
30215             zoom: this.zoom,
30216             mapTypeId: this.mapTypeId,
30217             mapTypeControl: this.mapTypeControl,
30218             disableDoubleClickZoom: this.disableDoubleClickZoom,
30219             scrollwheel: this.scrollwheel,
30220             streetViewControl: this.streetViewControl,
30221             locationName: this.locationName,
30222             draggable: this.draggable,
30223             enableAutocomplete: this.enableAutocomplete,
30224             enableReverseGeocode: this.enableReverseGeocode
30225         });
30226         
30227         var _marker = new google.maps.Marker({
30228             position: position,
30229             map: _map,
30230             title: this.markerTitle,
30231             draggable: this.draggable
30232         });
30233         
30234         return {
30235             map: _map,
30236             marker: _marker,
30237             circle: null,
30238             location: position,
30239             radius: this.radius,
30240             locationName: this.locationName,
30241             addressComponents: {
30242                 formatted_address: null,
30243                 addressLine1: null,
30244                 addressLine2: null,
30245                 streetName: null,
30246                 streetNumber: null,
30247                 city: null,
30248                 district: null,
30249                 state: null,
30250                 stateOrProvince: null
30251             },
30252             settings: this,
30253             domContainer: this.el.dom,
30254             geodecoder: new google.maps.Geocoder()
30255         };
30256     },
30257     
30258     drawCircle: function(center, radius, options) 
30259     {
30260         if (this.gMapContext.circle != null) {
30261             this.gMapContext.circle.setMap(null);
30262         }
30263         if (radius > 0) {
30264             radius *= 1;
30265             options = Roo.apply({}, options, {
30266                 strokeColor: "#0000FF",
30267                 strokeOpacity: .35,
30268                 strokeWeight: 2,
30269                 fillColor: "#0000FF",
30270                 fillOpacity: .2
30271             });
30272             
30273             options.map = this.gMapContext.map;
30274             options.radius = radius;
30275             options.center = center;
30276             this.gMapContext.circle = new google.maps.Circle(options);
30277             return this.gMapContext.circle;
30278         }
30279         
30280         return null;
30281     },
30282     
30283     setPosition: function(location) 
30284     {
30285         this.gMapContext.location = location;
30286         this.gMapContext.marker.setPosition(location);
30287         this.gMapContext.map.panTo(location);
30288         this.drawCircle(location, this.gMapContext.radius, {});
30289         
30290         var _this = this;
30291         
30292         if (this.gMapContext.settings.enableReverseGeocode) {
30293             this.gMapContext.geodecoder.geocode({
30294                 latLng: this.gMapContext.location
30295             }, function(results, status) {
30296                 
30297                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30298                     _this.gMapContext.locationName = results[0].formatted_address;
30299                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30300                     
30301                     _this.fireEvent('positionchanged', this, location);
30302                 }
30303             });
30304             
30305             return;
30306         }
30307         
30308         this.fireEvent('positionchanged', this, location);
30309     },
30310     
30311     resize: function()
30312     {
30313         google.maps.event.trigger(this.gMapContext.map, "resize");
30314         
30315         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30316         
30317         this.fireEvent('resize', this);
30318     },
30319     
30320     setPositionByLatLng: function(latitude, longitude)
30321     {
30322         this.setPosition(new google.maps.LatLng(latitude, longitude));
30323     },
30324     
30325     getCurrentPosition: function() 
30326     {
30327         return {
30328             latitude: this.gMapContext.location.lat(),
30329             longitude: this.gMapContext.location.lng()
30330         };
30331     },
30332     
30333     getAddressName: function() 
30334     {
30335         return this.gMapContext.locationName;
30336     },
30337     
30338     getAddressComponents: function() 
30339     {
30340         return this.gMapContext.addressComponents;
30341     },
30342     
30343     address_component_from_google_geocode: function(address_components) 
30344     {
30345         var result = {};
30346         
30347         for (var i = 0; i < address_components.length; i++) {
30348             var component = address_components[i];
30349             if (component.types.indexOf("postal_code") >= 0) {
30350                 result.postalCode = component.short_name;
30351             } else if (component.types.indexOf("street_number") >= 0) {
30352                 result.streetNumber = component.short_name;
30353             } else if (component.types.indexOf("route") >= 0) {
30354                 result.streetName = component.short_name;
30355             } else if (component.types.indexOf("neighborhood") >= 0) {
30356                 result.city = component.short_name;
30357             } else if (component.types.indexOf("locality") >= 0) {
30358                 result.city = component.short_name;
30359             } else if (component.types.indexOf("sublocality") >= 0) {
30360                 result.district = component.short_name;
30361             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30362                 result.stateOrProvince = component.short_name;
30363             } else if (component.types.indexOf("country") >= 0) {
30364                 result.country = component.short_name;
30365             }
30366         }
30367         
30368         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30369         result.addressLine2 = "";
30370         return result;
30371     },
30372     
30373     setZoomLevel: function(zoom)
30374     {
30375         this.gMapContext.map.setZoom(zoom);
30376     },
30377     
30378     show: function()
30379     {
30380         if(!this.el){
30381             return;
30382         }
30383         
30384         this.el.show();
30385         
30386         this.resize();
30387         
30388         this.fireEvent('show', this);
30389     },
30390     
30391     hide: function()
30392     {
30393         if(!this.el){
30394             return;
30395         }
30396         
30397         this.el.hide();
30398         
30399         this.fireEvent('hide', this);
30400     }
30401     
30402 });
30403
30404 Roo.apply(Roo.bootstrap.LocationPicker, {
30405     
30406     OverlayView : function(map, options)
30407     {
30408         options = options || {};
30409         
30410         this.setMap(map);
30411     }
30412     
30413     
30414 });/**
30415  * @class Roo.bootstrap.Alert
30416  * @extends Roo.bootstrap.Component
30417  * Bootstrap Alert class - shows an alert area box
30418  * eg
30419  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30420   Enter a valid email address
30421 </div>
30422  * @licence LGPL
30423  * @cfg {String} title The title of alert
30424  * @cfg {String} html The content of alert
30425  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30426  * @cfg {String} fa font-awesomeicon
30427  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30428  * @cfg {Boolean} close true to show a x closer
30429  * 
30430  * 
30431  * @constructor
30432  * Create a new alert
30433  * @param {Object} config The config object
30434  */
30435
30436
30437 Roo.bootstrap.Alert = function(config){
30438     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30439     
30440 };
30441
30442 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30443     
30444     title: '',
30445     html: '',
30446     weight: false,
30447     fa: false,
30448     faicon: false, // BC
30449     close : false,
30450     
30451     
30452     getAutoCreate : function()
30453     {
30454         
30455         var cfg = {
30456             tag : 'div',
30457             cls : 'alert',
30458             cn : [
30459                 {
30460                     tag: 'button',
30461                     type :  "button",
30462                     cls: "close",
30463                     html : '×',
30464                     style : this.close ? '' : 'display:none'
30465                 },
30466                 {
30467                     tag : 'i',
30468                     cls : 'roo-alert-icon'
30469                     
30470                 },
30471                 {
30472                     tag : 'b',
30473                     cls : 'roo-alert-title',
30474                     html : this.title
30475                 },
30476                 {
30477                     tag : 'span',
30478                     cls : 'roo-alert-text',
30479                     html : this.html
30480                 }
30481             ]
30482         };
30483         
30484         if(this.faicon){
30485             cfg.cn[0].cls += ' fa ' + this.faicon;
30486         }
30487         if(this.fa){
30488             cfg.cn[0].cls += ' fa ' + this.fa;
30489         }
30490         
30491         if(this.weight){
30492             cfg.cls += ' alert-' + this.weight;
30493         }
30494         
30495         return cfg;
30496     },
30497     
30498     initEvents: function() 
30499     {
30500         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30501         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30502         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30503         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30504         if (this.seconds > 0) {
30505             this.hide.defer(this.seconds, this);
30506         }
30507     },
30508     /**
30509      * Set the Title Message HTML
30510      * @param {String} html
30511      */
30512     setTitle : function(str)
30513     {
30514         this.titleEl.dom.innerHTML = str;
30515     },
30516      
30517      /**
30518      * Set the Body Message HTML
30519      * @param {String} html
30520      */
30521     setHtml : function(str)
30522     {
30523         this.htmlEl.dom.innerHTML = str;
30524     },
30525     /**
30526      * Set the Weight of the alert
30527      * @param {String} (success|info|warning|danger) weight
30528      */
30529     
30530     setWeight : function(weight)
30531     {
30532         if(this.weight){
30533             this.el.removeClass('alert-' + this.weight);
30534         }
30535         
30536         this.weight = weight;
30537         
30538         this.el.addClass('alert-' + this.weight);
30539     },
30540       /**
30541      * Set the Icon of the alert
30542      * @param {String} see fontawsome names (name without the 'fa-' bit)
30543      */
30544     setIcon : function(icon)
30545     {
30546         if(this.faicon){
30547             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30548         }
30549         
30550         this.faicon = icon;
30551         
30552         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30553     },
30554     /**
30555      * Hide the Alert
30556      */
30557     hide: function() 
30558     {
30559         this.el.hide();   
30560     },
30561     /**
30562      * Show the Alert
30563      */
30564     show: function() 
30565     {  
30566         this.el.show();   
30567     }
30568     
30569 });
30570
30571  
30572 /*
30573 * Licence: LGPL
30574 */
30575
30576 /**
30577  * @class Roo.bootstrap.UploadCropbox
30578  * @extends Roo.bootstrap.Component
30579  * Bootstrap UploadCropbox class
30580  * @cfg {String} emptyText show when image has been loaded
30581  * @cfg {String} rotateNotify show when image too small to rotate
30582  * @cfg {Number} errorTimeout default 3000
30583  * @cfg {Number} minWidth default 300
30584  * @cfg {Number} minHeight default 300
30585  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30586  * @cfg {Boolean} isDocument (true|false) default false
30587  * @cfg {String} url action url
30588  * @cfg {String} paramName default 'imageUpload'
30589  * @cfg {String} method default POST
30590  * @cfg {Boolean} loadMask (true|false) default true
30591  * @cfg {Boolean} loadingText default 'Loading...'
30592  * 
30593  * @constructor
30594  * Create a new UploadCropbox
30595  * @param {Object} config The config object
30596  */
30597
30598 Roo.bootstrap.UploadCropbox = function(config){
30599     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30600     
30601     this.addEvents({
30602         /**
30603          * @event beforeselectfile
30604          * Fire before select file
30605          * @param {Roo.bootstrap.UploadCropbox} this
30606          */
30607         "beforeselectfile" : true,
30608         /**
30609          * @event initial
30610          * Fire after initEvent
30611          * @param {Roo.bootstrap.UploadCropbox} this
30612          */
30613         "initial" : true,
30614         /**
30615          * @event crop
30616          * Fire after initEvent
30617          * @param {Roo.bootstrap.UploadCropbox} this
30618          * @param {String} data
30619          */
30620         "crop" : true,
30621         /**
30622          * @event prepare
30623          * Fire when preparing the file data
30624          * @param {Roo.bootstrap.UploadCropbox} this
30625          * @param {Object} file
30626          */
30627         "prepare" : true,
30628         /**
30629          * @event exception
30630          * Fire when get exception
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          * @param {XMLHttpRequest} xhr
30633          */
30634         "exception" : true,
30635         /**
30636          * @event beforeloadcanvas
30637          * Fire before load the canvas
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          * @param {String} src
30640          */
30641         "beforeloadcanvas" : true,
30642         /**
30643          * @event trash
30644          * Fire when trash image
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          */
30647         "trash" : true,
30648         /**
30649          * @event download
30650          * Fire when download the image
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          */
30653         "download" : true,
30654         /**
30655          * @event footerbuttonclick
30656          * Fire when footerbuttonclick
30657          * @param {Roo.bootstrap.UploadCropbox} this
30658          * @param {String} type
30659          */
30660         "footerbuttonclick" : true,
30661         /**
30662          * @event resize
30663          * Fire when resize
30664          * @param {Roo.bootstrap.UploadCropbox} this
30665          */
30666         "resize" : true,
30667         /**
30668          * @event rotate
30669          * Fire when rotate the image
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {String} pos
30672          */
30673         "rotate" : true,
30674         /**
30675          * @event inspect
30676          * Fire when inspect the file
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {Object} file
30679          */
30680         "inspect" : true,
30681         /**
30682          * @event upload
30683          * Fire when xhr upload the file
30684          * @param {Roo.bootstrap.UploadCropbox} this
30685          * @param {Object} data
30686          */
30687         "upload" : true,
30688         /**
30689          * @event arrange
30690          * Fire when arrange the file data
30691          * @param {Roo.bootstrap.UploadCropbox} this
30692          * @param {Object} formData
30693          */
30694         "arrange" : true
30695     });
30696     
30697     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30698 };
30699
30700 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30701     
30702     emptyText : 'Click to upload image',
30703     rotateNotify : 'Image is too small to rotate',
30704     errorTimeout : 3000,
30705     scale : 0,
30706     baseScale : 1,
30707     rotate : 0,
30708     dragable : false,
30709     pinching : false,
30710     mouseX : 0,
30711     mouseY : 0,
30712     cropData : false,
30713     minWidth : 300,
30714     minHeight : 300,
30715     file : false,
30716     exif : {},
30717     baseRotate : 1,
30718     cropType : 'image/jpeg',
30719     buttons : false,
30720     canvasLoaded : false,
30721     isDocument : false,
30722     method : 'POST',
30723     paramName : 'imageUpload',
30724     loadMask : true,
30725     loadingText : 'Loading...',
30726     maskEl : false,
30727     
30728     getAutoCreate : function()
30729     {
30730         var cfg = {
30731             tag : 'div',
30732             cls : 'roo-upload-cropbox',
30733             cn : [
30734                 {
30735                     tag : 'input',
30736                     cls : 'roo-upload-cropbox-selector',
30737                     type : 'file'
30738                 },
30739                 {
30740                     tag : 'div',
30741                     cls : 'roo-upload-cropbox-body',
30742                     style : 'cursor:pointer',
30743                     cn : [
30744                         {
30745                             tag : 'div',
30746                             cls : 'roo-upload-cropbox-preview'
30747                         },
30748                         {
30749                             tag : 'div',
30750                             cls : 'roo-upload-cropbox-thumb'
30751                         },
30752                         {
30753                             tag : 'div',
30754                             cls : 'roo-upload-cropbox-empty-notify',
30755                             html : this.emptyText
30756                         },
30757                         {
30758                             tag : 'div',
30759                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30760                             html : this.rotateNotify
30761                         }
30762                     ]
30763                 },
30764                 {
30765                     tag : 'div',
30766                     cls : 'roo-upload-cropbox-footer',
30767                     cn : {
30768                         tag : 'div',
30769                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30770                         cn : []
30771                     }
30772                 }
30773             ]
30774         };
30775         
30776         return cfg;
30777     },
30778     
30779     onRender : function(ct, position)
30780     {
30781         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30782         
30783         if (this.buttons.length) {
30784             
30785             Roo.each(this.buttons, function(bb) {
30786                 
30787                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30788                 
30789                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30790                 
30791             }, this);
30792         }
30793         
30794         if(this.loadMask){
30795             this.maskEl = this.el;
30796         }
30797     },
30798     
30799     initEvents : function()
30800     {
30801         this.urlAPI = (window.createObjectURL && window) || 
30802                                 (window.URL && URL.revokeObjectURL && URL) || 
30803                                 (window.webkitURL && webkitURL);
30804                         
30805         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30806         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30807         
30808         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30809         this.selectorEl.hide();
30810         
30811         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30812         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813         
30814         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30815         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30816         this.thumbEl.hide();
30817         
30818         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30819         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30820         
30821         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30822         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30823         this.errorEl.hide();
30824         
30825         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30826         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30827         this.footerEl.hide();
30828         
30829         this.setThumbBoxSize();
30830         
30831         this.bind();
30832         
30833         this.resize();
30834         
30835         this.fireEvent('initial', this);
30836     },
30837
30838     bind : function()
30839     {
30840         var _this = this;
30841         
30842         window.addEventListener("resize", function() { _this.resize(); } );
30843         
30844         this.bodyEl.on('click', this.beforeSelectFile, this);
30845         
30846         if(Roo.isTouch){
30847             this.bodyEl.on('touchstart', this.onTouchStart, this);
30848             this.bodyEl.on('touchmove', this.onTouchMove, this);
30849             this.bodyEl.on('touchend', this.onTouchEnd, this);
30850         }
30851         
30852         if(!Roo.isTouch){
30853             this.bodyEl.on('mousedown', this.onMouseDown, this);
30854             this.bodyEl.on('mousemove', this.onMouseMove, this);
30855             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30856             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30857             Roo.get(document).on('mouseup', this.onMouseUp, this);
30858         }
30859         
30860         this.selectorEl.on('change', this.onFileSelected, this);
30861     },
30862     
30863     reset : function()
30864     {    
30865         this.scale = 0;
30866         this.baseScale = 1;
30867         this.rotate = 0;
30868         this.baseRotate = 1;
30869         this.dragable = false;
30870         this.pinching = false;
30871         this.mouseX = 0;
30872         this.mouseY = 0;
30873         this.cropData = false;
30874         this.notifyEl.dom.innerHTML = this.emptyText;
30875         
30876         this.selectorEl.dom.value = '';
30877         
30878     },
30879     
30880     resize : function()
30881     {
30882         if(this.fireEvent('resize', this) != false){
30883             this.setThumbBoxPosition();
30884             this.setCanvasPosition();
30885         }
30886     },
30887     
30888     onFooterButtonClick : function(e, el, o, type)
30889     {
30890         switch (type) {
30891             case 'rotate-left' :
30892                 this.onRotateLeft(e);
30893                 break;
30894             case 'rotate-right' :
30895                 this.onRotateRight(e);
30896                 break;
30897             case 'picture' :
30898                 this.beforeSelectFile(e);
30899                 break;
30900             case 'trash' :
30901                 this.trash(e);
30902                 break;
30903             case 'crop' :
30904                 this.crop(e);
30905                 break;
30906             case 'download' :
30907                 this.download(e);
30908                 break;
30909             default :
30910                 break;
30911         }
30912         
30913         this.fireEvent('footerbuttonclick', this, type);
30914     },
30915     
30916     beforeSelectFile : function(e)
30917     {
30918         e.preventDefault();
30919         
30920         if(this.fireEvent('beforeselectfile', this) != false){
30921             this.selectorEl.dom.click();
30922         }
30923     },
30924     
30925     onFileSelected : function(e)
30926     {
30927         e.preventDefault();
30928         
30929         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30930             return;
30931         }
30932         
30933         var file = this.selectorEl.dom.files[0];
30934         
30935         if(this.fireEvent('inspect', this, file) != false){
30936             this.prepare(file);
30937         }
30938         
30939     },
30940     
30941     trash : function(e)
30942     {
30943         this.fireEvent('trash', this);
30944     },
30945     
30946     download : function(e)
30947     {
30948         this.fireEvent('download', this);
30949     },
30950     
30951     loadCanvas : function(src)
30952     {   
30953         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30954             
30955             this.reset();
30956             
30957             this.imageEl = document.createElement('img');
30958             
30959             var _this = this;
30960             
30961             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30962             
30963             this.imageEl.src = src;
30964         }
30965     },
30966     
30967     onLoadCanvas : function()
30968     {   
30969         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30970         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30971         
30972         this.bodyEl.un('click', this.beforeSelectFile, this);
30973         
30974         this.notifyEl.hide();
30975         this.thumbEl.show();
30976         this.footerEl.show();
30977         
30978         this.baseRotateLevel();
30979         
30980         if(this.isDocument){
30981             this.setThumbBoxSize();
30982         }
30983         
30984         this.setThumbBoxPosition();
30985         
30986         this.baseScaleLevel();
30987         
30988         this.draw();
30989         
30990         this.resize();
30991         
30992         this.canvasLoaded = true;
30993         
30994         if(this.loadMask){
30995             this.maskEl.unmask();
30996         }
30997         
30998     },
30999     
31000     setCanvasPosition : function()
31001     {   
31002         if(!this.canvasEl){
31003             return;
31004         }
31005         
31006         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31007         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31008         
31009         this.previewEl.setLeft(pw);
31010         this.previewEl.setTop(ph);
31011         
31012     },
31013     
31014     onMouseDown : function(e)
31015     {   
31016         e.stopEvent();
31017         
31018         this.dragable = true;
31019         this.pinching = false;
31020         
31021         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31022             this.dragable = false;
31023             return;
31024         }
31025         
31026         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31027         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31028         
31029     },
31030     
31031     onMouseMove : function(e)
31032     {   
31033         e.stopEvent();
31034         
31035         if(!this.canvasLoaded){
31036             return;
31037         }
31038         
31039         if (!this.dragable){
31040             return;
31041         }
31042         
31043         var minX = Math.ceil(this.thumbEl.getLeft(true));
31044         var minY = Math.ceil(this.thumbEl.getTop(true));
31045         
31046         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31047         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31048         
31049         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31050         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31051         
31052         x = x - this.mouseX;
31053         y = y - this.mouseY;
31054         
31055         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31056         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31057         
31058         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31059         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31060         
31061         this.previewEl.setLeft(bgX);
31062         this.previewEl.setTop(bgY);
31063         
31064         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31065         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31066     },
31067     
31068     onMouseUp : function(e)
31069     {   
31070         e.stopEvent();
31071         
31072         this.dragable = false;
31073     },
31074     
31075     onMouseWheel : function(e)
31076     {   
31077         e.stopEvent();
31078         
31079         this.startScale = this.scale;
31080         
31081         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31082         
31083         if(!this.zoomable()){
31084             this.scale = this.startScale;
31085             return;
31086         }
31087         
31088         this.draw();
31089         
31090         return;
31091     },
31092     
31093     zoomable : function()
31094     {
31095         var minScale = this.thumbEl.getWidth() / this.minWidth;
31096         
31097         if(this.minWidth < this.minHeight){
31098             minScale = this.thumbEl.getHeight() / this.minHeight;
31099         }
31100         
31101         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31102         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31103         
31104         if(
31105                 this.isDocument &&
31106                 (this.rotate == 0 || this.rotate == 180) && 
31107                 (
31108                     width > this.imageEl.OriginWidth || 
31109                     height > this.imageEl.OriginHeight ||
31110                     (width < this.minWidth && height < this.minHeight)
31111                 )
31112         ){
31113             return false;
31114         }
31115         
31116         if(
31117                 this.isDocument &&
31118                 (this.rotate == 90 || this.rotate == 270) && 
31119                 (
31120                     width > this.imageEl.OriginWidth || 
31121                     height > this.imageEl.OriginHeight ||
31122                     (width < this.minHeight && height < this.minWidth)
31123                 )
31124         ){
31125             return false;
31126         }
31127         
31128         if(
31129                 !this.isDocument &&
31130                 (this.rotate == 0 || this.rotate == 180) && 
31131                 (
31132                     width < this.minWidth || 
31133                     width > this.imageEl.OriginWidth || 
31134                     height < this.minHeight || 
31135                     height > this.imageEl.OriginHeight
31136                 )
31137         ){
31138             return false;
31139         }
31140         
31141         if(
31142                 !this.isDocument &&
31143                 (this.rotate == 90 || this.rotate == 270) && 
31144                 (
31145                     width < this.minHeight || 
31146                     width > this.imageEl.OriginWidth || 
31147                     height < this.minWidth || 
31148                     height > this.imageEl.OriginHeight
31149                 )
31150         ){
31151             return false;
31152         }
31153         
31154         return true;
31155         
31156     },
31157     
31158     onRotateLeft : function(e)
31159     {   
31160         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31161             
31162             var minScale = this.thumbEl.getWidth() / this.minWidth;
31163             
31164             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31165             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31166             
31167             this.startScale = this.scale;
31168             
31169             while (this.getScaleLevel() < minScale){
31170             
31171                 this.scale = this.scale + 1;
31172                 
31173                 if(!this.zoomable()){
31174                     break;
31175                 }
31176                 
31177                 if(
31178                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31179                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31180                 ){
31181                     continue;
31182                 }
31183                 
31184                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31185
31186                 this.draw();
31187                 
31188                 return;
31189             }
31190             
31191             this.scale = this.startScale;
31192             
31193             this.onRotateFail();
31194             
31195             return false;
31196         }
31197         
31198         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31199
31200         if(this.isDocument){
31201             this.setThumbBoxSize();
31202             this.setThumbBoxPosition();
31203             this.setCanvasPosition();
31204         }
31205         
31206         this.draw();
31207         
31208         this.fireEvent('rotate', this, 'left');
31209         
31210     },
31211     
31212     onRotateRight : function(e)
31213     {
31214         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31215             
31216             var minScale = this.thumbEl.getWidth() / this.minWidth;
31217         
31218             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31219             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31220             
31221             this.startScale = this.scale;
31222             
31223             while (this.getScaleLevel() < minScale){
31224             
31225                 this.scale = this.scale + 1;
31226                 
31227                 if(!this.zoomable()){
31228                     break;
31229                 }
31230                 
31231                 if(
31232                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31233                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31234                 ){
31235                     continue;
31236                 }
31237                 
31238                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31239
31240                 this.draw();
31241                 
31242                 return;
31243             }
31244             
31245             this.scale = this.startScale;
31246             
31247             this.onRotateFail();
31248             
31249             return false;
31250         }
31251         
31252         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31253
31254         if(this.isDocument){
31255             this.setThumbBoxSize();
31256             this.setThumbBoxPosition();
31257             this.setCanvasPosition();
31258         }
31259         
31260         this.draw();
31261         
31262         this.fireEvent('rotate', this, 'right');
31263     },
31264     
31265     onRotateFail : function()
31266     {
31267         this.errorEl.show(true);
31268         
31269         var _this = this;
31270         
31271         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31272     },
31273     
31274     draw : function()
31275     {
31276         this.previewEl.dom.innerHTML = '';
31277         
31278         var canvasEl = document.createElement("canvas");
31279         
31280         var contextEl = canvasEl.getContext("2d");
31281         
31282         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31283         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31284         var center = this.imageEl.OriginWidth / 2;
31285         
31286         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31287             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31288             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31289             center = this.imageEl.OriginHeight / 2;
31290         }
31291         
31292         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31293         
31294         contextEl.translate(center, center);
31295         contextEl.rotate(this.rotate * Math.PI / 180);
31296
31297         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31298         
31299         this.canvasEl = document.createElement("canvas");
31300         
31301         this.contextEl = this.canvasEl.getContext("2d");
31302         
31303         switch (this.rotate) {
31304             case 0 :
31305                 
31306                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31307                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31308                 
31309                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31310                 
31311                 break;
31312             case 90 : 
31313                 
31314                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31315                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31316                 
31317                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31318                     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);
31319                     break;
31320                 }
31321                 
31322                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31323                 
31324                 break;
31325             case 180 :
31326                 
31327                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31328                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31329                 
31330                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31331                     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);
31332                     break;
31333                 }
31334                 
31335                 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);
31336                 
31337                 break;
31338             case 270 :
31339                 
31340                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31341                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31342         
31343                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31344                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31345                     break;
31346                 }
31347                 
31348                 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);
31349                 
31350                 break;
31351             default : 
31352                 break;
31353         }
31354         
31355         this.previewEl.appendChild(this.canvasEl);
31356         
31357         this.setCanvasPosition();
31358     },
31359     
31360     crop : function()
31361     {
31362         if(!this.canvasLoaded){
31363             return;
31364         }
31365         
31366         var imageCanvas = document.createElement("canvas");
31367         
31368         var imageContext = imageCanvas.getContext("2d");
31369         
31370         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31371         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31372         
31373         var center = imageCanvas.width / 2;
31374         
31375         imageContext.translate(center, center);
31376         
31377         imageContext.rotate(this.rotate * Math.PI / 180);
31378         
31379         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31380         
31381         var canvas = document.createElement("canvas");
31382         
31383         var context = canvas.getContext("2d");
31384                 
31385         canvas.width = this.minWidth;
31386         canvas.height = this.minHeight;
31387
31388         switch (this.rotate) {
31389             case 0 :
31390                 
31391                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31392                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31393                 
31394                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31395                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31396                 
31397                 var targetWidth = this.minWidth - 2 * x;
31398                 var targetHeight = this.minHeight - 2 * y;
31399                 
31400                 var scale = 1;
31401                 
31402                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31403                     scale = targetWidth / width;
31404                 }
31405                 
31406                 if(x > 0 && y == 0){
31407                     scale = targetHeight / height;
31408                 }
31409                 
31410                 if(x > 0 && y > 0){
31411                     scale = targetWidth / width;
31412                     
31413                     if(width < height){
31414                         scale = targetHeight / height;
31415                     }
31416                 }
31417                 
31418                 context.scale(scale, scale);
31419                 
31420                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31421                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31422
31423                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31424                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31425
31426                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31427                 
31428                 break;
31429             case 90 : 
31430                 
31431                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31432                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31433                 
31434                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31435                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31436                 
31437                 var targetWidth = this.minWidth - 2 * x;
31438                 var targetHeight = this.minHeight - 2 * y;
31439                 
31440                 var scale = 1;
31441                 
31442                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31443                     scale = targetWidth / width;
31444                 }
31445                 
31446                 if(x > 0 && y == 0){
31447                     scale = targetHeight / height;
31448                 }
31449                 
31450                 if(x > 0 && y > 0){
31451                     scale = targetWidth / width;
31452                     
31453                     if(width < height){
31454                         scale = targetHeight / height;
31455                     }
31456                 }
31457                 
31458                 context.scale(scale, scale);
31459                 
31460                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31461                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31462
31463                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31464                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31465                 
31466                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31467                 
31468                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31469                 
31470                 break;
31471             case 180 :
31472                 
31473                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31474                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31475                 
31476                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31477                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31478                 
31479                 var targetWidth = this.minWidth - 2 * x;
31480                 var targetHeight = this.minHeight - 2 * y;
31481                 
31482                 var scale = 1;
31483                 
31484                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31485                     scale = targetWidth / width;
31486                 }
31487                 
31488                 if(x > 0 && y == 0){
31489                     scale = targetHeight / height;
31490                 }
31491                 
31492                 if(x > 0 && y > 0){
31493                     scale = targetWidth / width;
31494                     
31495                     if(width < height){
31496                         scale = targetHeight / height;
31497                     }
31498                 }
31499                 
31500                 context.scale(scale, scale);
31501                 
31502                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31503                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31504
31505                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31506                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31507
31508                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31509                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31510                 
31511                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31512                 
31513                 break;
31514             case 270 :
31515                 
31516                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31517                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31518                 
31519                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31520                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31521                 
31522                 var targetWidth = this.minWidth - 2 * x;
31523                 var targetHeight = this.minHeight - 2 * y;
31524                 
31525                 var scale = 1;
31526                 
31527                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31528                     scale = targetWidth / width;
31529                 }
31530                 
31531                 if(x > 0 && y == 0){
31532                     scale = targetHeight / height;
31533                 }
31534                 
31535                 if(x > 0 && y > 0){
31536                     scale = targetWidth / width;
31537                     
31538                     if(width < height){
31539                         scale = targetHeight / height;
31540                     }
31541                 }
31542                 
31543                 context.scale(scale, scale);
31544                 
31545                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31546                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31547
31548                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31549                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31550                 
31551                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31552                 
31553                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31554                 
31555                 break;
31556             default : 
31557                 break;
31558         }
31559         
31560         this.cropData = canvas.toDataURL(this.cropType);
31561         
31562         if(this.fireEvent('crop', this, this.cropData) !== false){
31563             this.process(this.file, this.cropData);
31564         }
31565         
31566         return;
31567         
31568     },
31569     
31570     setThumbBoxSize : function()
31571     {
31572         var width, height;
31573         
31574         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31575             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31576             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31577             
31578             this.minWidth = width;
31579             this.minHeight = height;
31580             
31581             if(this.rotate == 90 || this.rotate == 270){
31582                 this.minWidth = height;
31583                 this.minHeight = width;
31584             }
31585         }
31586         
31587         height = 300;
31588         width = Math.ceil(this.minWidth * height / this.minHeight);
31589         
31590         if(this.minWidth > this.minHeight){
31591             width = 300;
31592             height = Math.ceil(this.minHeight * width / this.minWidth);
31593         }
31594         
31595         this.thumbEl.setStyle({
31596             width : width + 'px',
31597             height : height + 'px'
31598         });
31599
31600         return;
31601             
31602     },
31603     
31604     setThumbBoxPosition : function()
31605     {
31606         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31607         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31608         
31609         this.thumbEl.setLeft(x);
31610         this.thumbEl.setTop(y);
31611         
31612     },
31613     
31614     baseRotateLevel : function()
31615     {
31616         this.baseRotate = 1;
31617         
31618         if(
31619                 typeof(this.exif) != 'undefined' &&
31620                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31621                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31622         ){
31623             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31624         }
31625         
31626         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31627         
31628     },
31629     
31630     baseScaleLevel : function()
31631     {
31632         var width, height;
31633         
31634         if(this.isDocument){
31635             
31636             if(this.baseRotate == 6 || this.baseRotate == 8){
31637             
31638                 height = this.thumbEl.getHeight();
31639                 this.baseScale = height / this.imageEl.OriginWidth;
31640
31641                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31642                     width = this.thumbEl.getWidth();
31643                     this.baseScale = width / this.imageEl.OriginHeight;
31644                 }
31645
31646                 return;
31647             }
31648
31649             height = this.thumbEl.getHeight();
31650             this.baseScale = height / this.imageEl.OriginHeight;
31651
31652             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31653                 width = this.thumbEl.getWidth();
31654                 this.baseScale = width / this.imageEl.OriginWidth;
31655             }
31656
31657             return;
31658         }
31659         
31660         if(this.baseRotate == 6 || this.baseRotate == 8){
31661             
31662             width = this.thumbEl.getHeight();
31663             this.baseScale = width / this.imageEl.OriginHeight;
31664             
31665             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31666                 height = this.thumbEl.getWidth();
31667                 this.baseScale = height / this.imageEl.OriginHeight;
31668             }
31669             
31670             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31671                 height = this.thumbEl.getWidth();
31672                 this.baseScale = height / this.imageEl.OriginHeight;
31673                 
31674                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31675                     width = this.thumbEl.getHeight();
31676                     this.baseScale = width / this.imageEl.OriginWidth;
31677                 }
31678             }
31679             
31680             return;
31681         }
31682         
31683         width = this.thumbEl.getWidth();
31684         this.baseScale = width / this.imageEl.OriginWidth;
31685         
31686         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31687             height = this.thumbEl.getHeight();
31688             this.baseScale = height / this.imageEl.OriginHeight;
31689         }
31690         
31691         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31692             
31693             height = this.thumbEl.getHeight();
31694             this.baseScale = height / this.imageEl.OriginHeight;
31695             
31696             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31697                 width = this.thumbEl.getWidth();
31698                 this.baseScale = width / this.imageEl.OriginWidth;
31699             }
31700             
31701         }
31702         
31703         return;
31704     },
31705     
31706     getScaleLevel : function()
31707     {
31708         return this.baseScale * Math.pow(1.1, this.scale);
31709     },
31710     
31711     onTouchStart : function(e)
31712     {
31713         if(!this.canvasLoaded){
31714             this.beforeSelectFile(e);
31715             return;
31716         }
31717         
31718         var touches = e.browserEvent.touches;
31719         
31720         if(!touches){
31721             return;
31722         }
31723         
31724         if(touches.length == 1){
31725             this.onMouseDown(e);
31726             return;
31727         }
31728         
31729         if(touches.length != 2){
31730             return;
31731         }
31732         
31733         var coords = [];
31734         
31735         for(var i = 0, finger; finger = touches[i]; i++){
31736             coords.push(finger.pageX, finger.pageY);
31737         }
31738         
31739         var x = Math.pow(coords[0] - coords[2], 2);
31740         var y = Math.pow(coords[1] - coords[3], 2);
31741         
31742         this.startDistance = Math.sqrt(x + y);
31743         
31744         this.startScale = this.scale;
31745         
31746         this.pinching = true;
31747         this.dragable = false;
31748         
31749     },
31750     
31751     onTouchMove : function(e)
31752     {
31753         if(!this.pinching && !this.dragable){
31754             return;
31755         }
31756         
31757         var touches = e.browserEvent.touches;
31758         
31759         if(!touches){
31760             return;
31761         }
31762         
31763         if(this.dragable){
31764             this.onMouseMove(e);
31765             return;
31766         }
31767         
31768         var coords = [];
31769         
31770         for(var i = 0, finger; finger = touches[i]; i++){
31771             coords.push(finger.pageX, finger.pageY);
31772         }
31773         
31774         var x = Math.pow(coords[0] - coords[2], 2);
31775         var y = Math.pow(coords[1] - coords[3], 2);
31776         
31777         this.endDistance = Math.sqrt(x + y);
31778         
31779         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31780         
31781         if(!this.zoomable()){
31782             this.scale = this.startScale;
31783             return;
31784         }
31785         
31786         this.draw();
31787         
31788     },
31789     
31790     onTouchEnd : function(e)
31791     {
31792         this.pinching = false;
31793         this.dragable = false;
31794         
31795     },
31796     
31797     process : function(file, crop)
31798     {
31799         if(this.loadMask){
31800             this.maskEl.mask(this.loadingText);
31801         }
31802         
31803         this.xhr = new XMLHttpRequest();
31804         
31805         file.xhr = this.xhr;
31806
31807         this.xhr.open(this.method, this.url, true);
31808         
31809         var headers = {
31810             "Accept": "application/json",
31811             "Cache-Control": "no-cache",
31812             "X-Requested-With": "XMLHttpRequest"
31813         };
31814         
31815         for (var headerName in headers) {
31816             var headerValue = headers[headerName];
31817             if (headerValue) {
31818                 this.xhr.setRequestHeader(headerName, headerValue);
31819             }
31820         }
31821         
31822         var _this = this;
31823         
31824         this.xhr.onload = function()
31825         {
31826             _this.xhrOnLoad(_this.xhr);
31827         }
31828         
31829         this.xhr.onerror = function()
31830         {
31831             _this.xhrOnError(_this.xhr);
31832         }
31833         
31834         var formData = new FormData();
31835
31836         formData.append('returnHTML', 'NO');
31837         
31838         if(crop){
31839             formData.append('crop', crop);
31840         }
31841         
31842         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31843             formData.append(this.paramName, file, file.name);
31844         }
31845         
31846         if(typeof(file.filename) != 'undefined'){
31847             formData.append('filename', file.filename);
31848         }
31849         
31850         if(typeof(file.mimetype) != 'undefined'){
31851             formData.append('mimetype', file.mimetype);
31852         }
31853         
31854         if(this.fireEvent('arrange', this, formData) != false){
31855             this.xhr.send(formData);
31856         };
31857     },
31858     
31859     xhrOnLoad : function(xhr)
31860     {
31861         if(this.loadMask){
31862             this.maskEl.unmask();
31863         }
31864         
31865         if (xhr.readyState !== 4) {
31866             this.fireEvent('exception', this, xhr);
31867             return;
31868         }
31869
31870         var response = Roo.decode(xhr.responseText);
31871         
31872         if(!response.success){
31873             this.fireEvent('exception', this, xhr);
31874             return;
31875         }
31876         
31877         var response = Roo.decode(xhr.responseText);
31878         
31879         this.fireEvent('upload', this, response);
31880         
31881     },
31882     
31883     xhrOnError : function()
31884     {
31885         if(this.loadMask){
31886             this.maskEl.unmask();
31887         }
31888         
31889         Roo.log('xhr on error');
31890         
31891         var response = Roo.decode(xhr.responseText);
31892           
31893         Roo.log(response);
31894         
31895     },
31896     
31897     prepare : function(file)
31898     {   
31899         if(this.loadMask){
31900             this.maskEl.mask(this.loadingText);
31901         }
31902         
31903         this.file = false;
31904         this.exif = {};
31905         
31906         if(typeof(file) === 'string'){
31907             this.loadCanvas(file);
31908             return;
31909         }
31910         
31911         if(!file || !this.urlAPI){
31912             return;
31913         }
31914         
31915         this.file = file;
31916         this.cropType = file.type;
31917         
31918         var _this = this;
31919         
31920         if(this.fireEvent('prepare', this, this.file) != false){
31921             
31922             var reader = new FileReader();
31923             
31924             reader.onload = function (e) {
31925                 if (e.target.error) {
31926                     Roo.log(e.target.error);
31927                     return;
31928                 }
31929                 
31930                 var buffer = e.target.result,
31931                     dataView = new DataView(buffer),
31932                     offset = 2,
31933                     maxOffset = dataView.byteLength - 4,
31934                     markerBytes,
31935                     markerLength;
31936                 
31937                 if (dataView.getUint16(0) === 0xffd8) {
31938                     while (offset < maxOffset) {
31939                         markerBytes = dataView.getUint16(offset);
31940                         
31941                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31942                             markerLength = dataView.getUint16(offset + 2) + 2;
31943                             if (offset + markerLength > dataView.byteLength) {
31944                                 Roo.log('Invalid meta data: Invalid segment size.');
31945                                 break;
31946                             }
31947                             
31948                             if(markerBytes == 0xffe1){
31949                                 _this.parseExifData(
31950                                     dataView,
31951                                     offset,
31952                                     markerLength
31953                                 );
31954                             }
31955                             
31956                             offset += markerLength;
31957                             
31958                             continue;
31959                         }
31960                         
31961                         break;
31962                     }
31963                     
31964                 }
31965                 
31966                 var url = _this.urlAPI.createObjectURL(_this.file);
31967                 
31968                 _this.loadCanvas(url);
31969                 
31970                 return;
31971             }
31972             
31973             reader.readAsArrayBuffer(this.file);
31974             
31975         }
31976         
31977     },
31978     
31979     parseExifData : function(dataView, offset, length)
31980     {
31981         var tiffOffset = offset + 10,
31982             littleEndian,
31983             dirOffset;
31984     
31985         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31986             // No Exif data, might be XMP data instead
31987             return;
31988         }
31989         
31990         // Check for the ASCII code for "Exif" (0x45786966):
31991         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31992             // No Exif data, might be XMP data instead
31993             return;
31994         }
31995         if (tiffOffset + 8 > dataView.byteLength) {
31996             Roo.log('Invalid Exif data: Invalid segment size.');
31997             return;
31998         }
31999         // Check for the two null bytes:
32000         if (dataView.getUint16(offset + 8) !== 0x0000) {
32001             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32002             return;
32003         }
32004         // Check the byte alignment:
32005         switch (dataView.getUint16(tiffOffset)) {
32006         case 0x4949:
32007             littleEndian = true;
32008             break;
32009         case 0x4D4D:
32010             littleEndian = false;
32011             break;
32012         default:
32013             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32014             return;
32015         }
32016         // Check for the TIFF tag marker (0x002A):
32017         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32018             Roo.log('Invalid Exif data: Missing TIFF marker.');
32019             return;
32020         }
32021         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32022         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32023         
32024         this.parseExifTags(
32025             dataView,
32026             tiffOffset,
32027             tiffOffset + dirOffset,
32028             littleEndian
32029         );
32030     },
32031     
32032     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32033     {
32034         var tagsNumber,
32035             dirEndOffset,
32036             i;
32037         if (dirOffset + 6 > dataView.byteLength) {
32038             Roo.log('Invalid Exif data: Invalid directory offset.');
32039             return;
32040         }
32041         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32042         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32043         if (dirEndOffset + 4 > dataView.byteLength) {
32044             Roo.log('Invalid Exif data: Invalid directory size.');
32045             return;
32046         }
32047         for (i = 0; i < tagsNumber; i += 1) {
32048             this.parseExifTag(
32049                 dataView,
32050                 tiffOffset,
32051                 dirOffset + 2 + 12 * i, // tag offset
32052                 littleEndian
32053             );
32054         }
32055         // Return the offset to the next directory:
32056         return dataView.getUint32(dirEndOffset, littleEndian);
32057     },
32058     
32059     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32060     {
32061         var tag = dataView.getUint16(offset, littleEndian);
32062         
32063         this.exif[tag] = this.getExifValue(
32064             dataView,
32065             tiffOffset,
32066             offset,
32067             dataView.getUint16(offset + 2, littleEndian), // tag type
32068             dataView.getUint32(offset + 4, littleEndian), // tag length
32069             littleEndian
32070         );
32071     },
32072     
32073     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32074     {
32075         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32076             tagSize,
32077             dataOffset,
32078             values,
32079             i,
32080             str,
32081             c;
32082     
32083         if (!tagType) {
32084             Roo.log('Invalid Exif data: Invalid tag type.');
32085             return;
32086         }
32087         
32088         tagSize = tagType.size * length;
32089         // Determine if the value is contained in the dataOffset bytes,
32090         // or if the value at the dataOffset is a pointer to the actual data:
32091         dataOffset = tagSize > 4 ?
32092                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32093         if (dataOffset + tagSize > dataView.byteLength) {
32094             Roo.log('Invalid Exif data: Invalid data offset.');
32095             return;
32096         }
32097         if (length === 1) {
32098             return tagType.getValue(dataView, dataOffset, littleEndian);
32099         }
32100         values = [];
32101         for (i = 0; i < length; i += 1) {
32102             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32103         }
32104         
32105         if (tagType.ascii) {
32106             str = '';
32107             // Concatenate the chars:
32108             for (i = 0; i < values.length; i += 1) {
32109                 c = values[i];
32110                 // Ignore the terminating NULL byte(s):
32111                 if (c === '\u0000') {
32112                     break;
32113                 }
32114                 str += c;
32115             }
32116             return str;
32117         }
32118         return values;
32119     }
32120     
32121 });
32122
32123 Roo.apply(Roo.bootstrap.UploadCropbox, {
32124     tags : {
32125         'Orientation': 0x0112
32126     },
32127     
32128     Orientation: {
32129             1: 0, //'top-left',
32130 //            2: 'top-right',
32131             3: 180, //'bottom-right',
32132 //            4: 'bottom-left',
32133 //            5: 'left-top',
32134             6: 90, //'right-top',
32135 //            7: 'right-bottom',
32136             8: 270 //'left-bottom'
32137     },
32138     
32139     exifTagTypes : {
32140         // byte, 8-bit unsigned int:
32141         1: {
32142             getValue: function (dataView, dataOffset) {
32143                 return dataView.getUint8(dataOffset);
32144             },
32145             size: 1
32146         },
32147         // ascii, 8-bit byte:
32148         2: {
32149             getValue: function (dataView, dataOffset) {
32150                 return String.fromCharCode(dataView.getUint8(dataOffset));
32151             },
32152             size: 1,
32153             ascii: true
32154         },
32155         // short, 16 bit int:
32156         3: {
32157             getValue: function (dataView, dataOffset, littleEndian) {
32158                 return dataView.getUint16(dataOffset, littleEndian);
32159             },
32160             size: 2
32161         },
32162         // long, 32 bit int:
32163         4: {
32164             getValue: function (dataView, dataOffset, littleEndian) {
32165                 return dataView.getUint32(dataOffset, littleEndian);
32166             },
32167             size: 4
32168         },
32169         // rational = two long values, first is numerator, second is denominator:
32170         5: {
32171             getValue: function (dataView, dataOffset, littleEndian) {
32172                 return dataView.getUint32(dataOffset, littleEndian) /
32173                     dataView.getUint32(dataOffset + 4, littleEndian);
32174             },
32175             size: 8
32176         },
32177         // slong, 32 bit signed int:
32178         9: {
32179             getValue: function (dataView, dataOffset, littleEndian) {
32180                 return dataView.getInt32(dataOffset, littleEndian);
32181             },
32182             size: 4
32183         },
32184         // srational, two slongs, first is numerator, second is denominator:
32185         10: {
32186             getValue: function (dataView, dataOffset, littleEndian) {
32187                 return dataView.getInt32(dataOffset, littleEndian) /
32188                     dataView.getInt32(dataOffset + 4, littleEndian);
32189             },
32190             size: 8
32191         }
32192     },
32193     
32194     footer : {
32195         STANDARD : [
32196             {
32197                 tag : 'div',
32198                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32199                 action : 'rotate-left',
32200                 cn : [
32201                     {
32202                         tag : 'button',
32203                         cls : 'btn btn-default',
32204                         html : '<i class="fa fa-undo"></i>'
32205                     }
32206                 ]
32207             },
32208             {
32209                 tag : 'div',
32210                 cls : 'btn-group roo-upload-cropbox-picture',
32211                 action : 'picture',
32212                 cn : [
32213                     {
32214                         tag : 'button',
32215                         cls : 'btn btn-default',
32216                         html : '<i class="fa fa-picture-o"></i>'
32217                     }
32218                 ]
32219             },
32220             {
32221                 tag : 'div',
32222                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32223                 action : 'rotate-right',
32224                 cn : [
32225                     {
32226                         tag : 'button',
32227                         cls : 'btn btn-default',
32228                         html : '<i class="fa fa-repeat"></i>'
32229                     }
32230                 ]
32231             }
32232         ],
32233         DOCUMENT : [
32234             {
32235                 tag : 'div',
32236                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32237                 action : 'rotate-left',
32238                 cn : [
32239                     {
32240                         tag : 'button',
32241                         cls : 'btn btn-default',
32242                         html : '<i class="fa fa-undo"></i>'
32243                     }
32244                 ]
32245             },
32246             {
32247                 tag : 'div',
32248                 cls : 'btn-group roo-upload-cropbox-download',
32249                 action : 'download',
32250                 cn : [
32251                     {
32252                         tag : 'button',
32253                         cls : 'btn btn-default',
32254                         html : '<i class="fa fa-download"></i>'
32255                     }
32256                 ]
32257             },
32258             {
32259                 tag : 'div',
32260                 cls : 'btn-group roo-upload-cropbox-crop',
32261                 action : 'crop',
32262                 cn : [
32263                     {
32264                         tag : 'button',
32265                         cls : 'btn btn-default',
32266                         html : '<i class="fa fa-crop"></i>'
32267                     }
32268                 ]
32269             },
32270             {
32271                 tag : 'div',
32272                 cls : 'btn-group roo-upload-cropbox-trash',
32273                 action : 'trash',
32274                 cn : [
32275                     {
32276                         tag : 'button',
32277                         cls : 'btn btn-default',
32278                         html : '<i class="fa fa-trash"></i>'
32279                     }
32280                 ]
32281             },
32282             {
32283                 tag : 'div',
32284                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32285                 action : 'rotate-right',
32286                 cn : [
32287                     {
32288                         tag : 'button',
32289                         cls : 'btn btn-default',
32290                         html : '<i class="fa fa-repeat"></i>'
32291                     }
32292                 ]
32293             }
32294         ],
32295         ROTATOR : [
32296             {
32297                 tag : 'div',
32298                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32299                 action : 'rotate-left',
32300                 cn : [
32301                     {
32302                         tag : 'button',
32303                         cls : 'btn btn-default',
32304                         html : '<i class="fa fa-undo"></i>'
32305                     }
32306                 ]
32307             },
32308             {
32309                 tag : 'div',
32310                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32311                 action : 'rotate-right',
32312                 cn : [
32313                     {
32314                         tag : 'button',
32315                         cls : 'btn btn-default',
32316                         html : '<i class="fa fa-repeat"></i>'
32317                     }
32318                 ]
32319             }
32320         ]
32321     }
32322 });
32323
32324 /*
32325 * Licence: LGPL
32326 */
32327
32328 /**
32329  * @class Roo.bootstrap.DocumentManager
32330  * @extends Roo.bootstrap.Component
32331  * Bootstrap DocumentManager class
32332  * @cfg {String} paramName default 'imageUpload'
32333  * @cfg {String} toolTipName default 'filename'
32334  * @cfg {String} method default POST
32335  * @cfg {String} url action url
32336  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32337  * @cfg {Boolean} multiple multiple upload default true
32338  * @cfg {Number} thumbSize default 300
32339  * @cfg {String} fieldLabel
32340  * @cfg {Number} labelWidth default 4
32341  * @cfg {String} labelAlign (left|top) default left
32342  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32343 * @cfg {Number} labellg set the width of label (1-12)
32344  * @cfg {Number} labelmd set the width of label (1-12)
32345  * @cfg {Number} labelsm set the width of label (1-12)
32346  * @cfg {Number} labelxs set the width of label (1-12)
32347  * 
32348  * @constructor
32349  * Create a new DocumentManager
32350  * @param {Object} config The config object
32351  */
32352
32353 Roo.bootstrap.DocumentManager = function(config){
32354     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32355     
32356     this.files = [];
32357     this.delegates = [];
32358     
32359     this.addEvents({
32360         /**
32361          * @event initial
32362          * Fire when initial the DocumentManager
32363          * @param {Roo.bootstrap.DocumentManager} this
32364          */
32365         "initial" : true,
32366         /**
32367          * @event inspect
32368          * inspect selected file
32369          * @param {Roo.bootstrap.DocumentManager} this
32370          * @param {File} file
32371          */
32372         "inspect" : true,
32373         /**
32374          * @event exception
32375          * Fire when xhr load exception
32376          * @param {Roo.bootstrap.DocumentManager} this
32377          * @param {XMLHttpRequest} xhr
32378          */
32379         "exception" : true,
32380         /**
32381          * @event afterupload
32382          * Fire when xhr load exception
32383          * @param {Roo.bootstrap.DocumentManager} this
32384          * @param {XMLHttpRequest} xhr
32385          */
32386         "afterupload" : true,
32387         /**
32388          * @event prepare
32389          * prepare the form data
32390          * @param {Roo.bootstrap.DocumentManager} this
32391          * @param {Object} formData
32392          */
32393         "prepare" : true,
32394         /**
32395          * @event remove
32396          * Fire when remove the file
32397          * @param {Roo.bootstrap.DocumentManager} this
32398          * @param {Object} file
32399          */
32400         "remove" : true,
32401         /**
32402          * @event refresh
32403          * Fire after refresh the file
32404          * @param {Roo.bootstrap.DocumentManager} this
32405          */
32406         "refresh" : true,
32407         /**
32408          * @event click
32409          * Fire after click the image
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          * @param {Object} file
32412          */
32413         "click" : true,
32414         /**
32415          * @event edit
32416          * Fire when upload a image and editable set to true
32417          * @param {Roo.bootstrap.DocumentManager} this
32418          * @param {Object} file
32419          */
32420         "edit" : true,
32421         /**
32422          * @event beforeselectfile
32423          * Fire before select file
32424          * @param {Roo.bootstrap.DocumentManager} this
32425          */
32426         "beforeselectfile" : true,
32427         /**
32428          * @event process
32429          * Fire before process file
32430          * @param {Roo.bootstrap.DocumentManager} this
32431          * @param {Object} file
32432          */
32433         "process" : true,
32434         /**
32435          * @event previewrendered
32436          * Fire when preview rendered
32437          * @param {Roo.bootstrap.DocumentManager} this
32438          * @param {Object} file
32439          */
32440         "previewrendered" : true,
32441         /**
32442          */
32443         "previewResize" : true
32444         
32445     });
32446 };
32447
32448 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32449     
32450     boxes : 0,
32451     inputName : '',
32452     thumbSize : 300,
32453     multiple : true,
32454     files : false,
32455     method : 'POST',
32456     url : '',
32457     paramName : 'imageUpload',
32458     toolTipName : 'filename',
32459     fieldLabel : '',
32460     labelWidth : 4,
32461     labelAlign : 'left',
32462     editable : true,
32463     delegates : false,
32464     xhr : false, 
32465     
32466     labellg : 0,
32467     labelmd : 0,
32468     labelsm : 0,
32469     labelxs : 0,
32470     
32471     getAutoCreate : function()
32472     {   
32473         var managerWidget = {
32474             tag : 'div',
32475             cls : 'roo-document-manager',
32476             cn : [
32477                 {
32478                     tag : 'input',
32479                     cls : 'roo-document-manager-selector',
32480                     type : 'file'
32481                 },
32482                 {
32483                     tag : 'div',
32484                     cls : 'roo-document-manager-uploader',
32485                     cn : [
32486                         {
32487                             tag : 'div',
32488                             cls : 'roo-document-manager-upload-btn',
32489                             html : '<i class="fa fa-plus"></i>'
32490                         }
32491                     ]
32492                     
32493                 }
32494             ]
32495         };
32496         
32497         var content = [
32498             {
32499                 tag : 'div',
32500                 cls : 'column col-md-12',
32501                 cn : managerWidget
32502             }
32503         ];
32504         
32505         if(this.fieldLabel.length){
32506             
32507             content = [
32508                 {
32509                     tag : 'div',
32510                     cls : 'column col-md-12',
32511                     html : this.fieldLabel
32512                 },
32513                 {
32514                     tag : 'div',
32515                     cls : 'column col-md-12',
32516                     cn : managerWidget
32517                 }
32518             ];
32519
32520             if(this.labelAlign == 'left'){
32521                 content = [
32522                     {
32523                         tag : 'div',
32524                         cls : 'column',
32525                         html : this.fieldLabel
32526                     },
32527                     {
32528                         tag : 'div',
32529                         cls : 'column',
32530                         cn : managerWidget
32531                     }
32532                 ];
32533                 
32534                 if(this.labelWidth > 12){
32535                     content[0].style = "width: " + this.labelWidth + 'px';
32536                 }
32537
32538                 if(this.labelWidth < 13 && this.labelmd == 0){
32539                     this.labelmd = this.labelWidth;
32540                 }
32541
32542                 if(this.labellg > 0){
32543                     content[0].cls += ' col-lg-' + this.labellg;
32544                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32545                 }
32546
32547                 if(this.labelmd > 0){
32548                     content[0].cls += ' col-md-' + this.labelmd;
32549                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32550                 }
32551
32552                 if(this.labelsm > 0){
32553                     content[0].cls += ' col-sm-' + this.labelsm;
32554                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32555                 }
32556
32557                 if(this.labelxs > 0){
32558                     content[0].cls += ' col-xs-' + this.labelxs;
32559                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32560                 }
32561                 
32562             }
32563         }
32564         
32565         var cfg = {
32566             tag : 'div',
32567             cls : 'row clearfix',
32568             cn : content
32569         };
32570         
32571         return cfg;
32572         
32573     },
32574     
32575     initEvents : function()
32576     {
32577         this.managerEl = this.el.select('.roo-document-manager', true).first();
32578         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32579         
32580         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32581         this.selectorEl.hide();
32582         
32583         if(this.multiple){
32584             this.selectorEl.attr('multiple', 'multiple');
32585         }
32586         
32587         this.selectorEl.on('change', this.onFileSelected, this);
32588         
32589         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32590         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32591         
32592         this.uploader.on('click', this.onUploaderClick, this);
32593         
32594         this.renderProgressDialog();
32595         
32596         var _this = this;
32597         
32598         window.addEventListener("resize", function() { _this.refresh(); } );
32599         
32600         this.fireEvent('initial', this);
32601     },
32602     
32603     renderProgressDialog : function()
32604     {
32605         var _this = this;
32606         
32607         this.progressDialog = new Roo.bootstrap.Modal({
32608             cls : 'roo-document-manager-progress-dialog',
32609             allow_close : false,
32610             animate : false,
32611             title : '',
32612             buttons : [
32613                 {
32614                     name  :'cancel',
32615                     weight : 'danger',
32616                     html : 'Cancel'
32617                 }
32618             ], 
32619             listeners : { 
32620                 btnclick : function() {
32621                     _this.uploadCancel();
32622                     this.hide();
32623                 }
32624             }
32625         });
32626          
32627         this.progressDialog.render(Roo.get(document.body));
32628          
32629         this.progress = new Roo.bootstrap.Progress({
32630             cls : 'roo-document-manager-progress',
32631             active : true,
32632             striped : true
32633         });
32634         
32635         this.progress.render(this.progressDialog.getChildContainer());
32636         
32637         this.progressBar = new Roo.bootstrap.ProgressBar({
32638             cls : 'roo-document-manager-progress-bar',
32639             aria_valuenow : 0,
32640             aria_valuemin : 0,
32641             aria_valuemax : 12,
32642             panel : 'success'
32643         });
32644         
32645         this.progressBar.render(this.progress.getChildContainer());
32646     },
32647     
32648     onUploaderClick : function(e)
32649     {
32650         e.preventDefault();
32651      
32652         if(this.fireEvent('beforeselectfile', this) != false){
32653             this.selectorEl.dom.click();
32654         }
32655         
32656     },
32657     
32658     onFileSelected : function(e)
32659     {
32660         e.preventDefault();
32661         
32662         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32663             return;
32664         }
32665         
32666         Roo.each(this.selectorEl.dom.files, function(file){
32667             if(this.fireEvent('inspect', this, file) != false){
32668                 this.files.push(file);
32669             }
32670         }, this);
32671         
32672         this.queue();
32673         
32674     },
32675     
32676     queue : function()
32677     {
32678         this.selectorEl.dom.value = '';
32679         
32680         if(!this.files || !this.files.length){
32681             return;
32682         }
32683         
32684         if(this.boxes > 0 && this.files.length > this.boxes){
32685             this.files = this.files.slice(0, this.boxes);
32686         }
32687         
32688         this.uploader.show();
32689         
32690         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32691             this.uploader.hide();
32692         }
32693         
32694         var _this = this;
32695         
32696         var files = [];
32697         
32698         var docs = [];
32699         
32700         Roo.each(this.files, function(file){
32701             
32702             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32703                 var f = this.renderPreview(file);
32704                 files.push(f);
32705                 return;
32706             }
32707             
32708             if(file.type.indexOf('image') != -1){
32709                 this.delegates.push(
32710                     (function(){
32711                         _this.process(file);
32712                     }).createDelegate(this)
32713                 );
32714         
32715                 return;
32716             }
32717             
32718             docs.push(
32719                 (function(){
32720                     _this.process(file);
32721                 }).createDelegate(this)
32722             );
32723             
32724         }, this);
32725         
32726         this.files = files;
32727         
32728         this.delegates = this.delegates.concat(docs);
32729         
32730         if(!this.delegates.length){
32731             this.refresh();
32732             return;
32733         }
32734         
32735         this.progressBar.aria_valuemax = this.delegates.length;
32736         
32737         this.arrange();
32738         
32739         return;
32740     },
32741     
32742     arrange : function()
32743     {
32744         if(!this.delegates.length){
32745             this.progressDialog.hide();
32746             this.refresh();
32747             return;
32748         }
32749         
32750         var delegate = this.delegates.shift();
32751         
32752         this.progressDialog.show();
32753         
32754         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32755         
32756         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32757         
32758         delegate();
32759     },
32760     
32761     refresh : function()
32762     {
32763         this.uploader.show();
32764         
32765         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32766             this.uploader.hide();
32767         }
32768         
32769         Roo.isTouch ? this.closable(false) : this.closable(true);
32770         
32771         this.fireEvent('refresh', this);
32772     },
32773     
32774     onRemove : function(e, el, o)
32775     {
32776         e.preventDefault();
32777         
32778         this.fireEvent('remove', this, o);
32779         
32780     },
32781     
32782     remove : function(o)
32783     {
32784         var files = [];
32785         
32786         Roo.each(this.files, function(file){
32787             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32788                 files.push(file);
32789                 return;
32790             }
32791
32792             o.target.remove();
32793
32794         }, this);
32795         
32796         this.files = files;
32797         
32798         this.refresh();
32799     },
32800     
32801     clear : function()
32802     {
32803         Roo.each(this.files, function(file){
32804             if(!file.target){
32805                 return;
32806             }
32807             
32808             file.target.remove();
32809
32810         }, this);
32811         
32812         this.files = [];
32813         
32814         this.refresh();
32815     },
32816     
32817     onClick : function(e, el, o)
32818     {
32819         e.preventDefault();
32820         
32821         this.fireEvent('click', this, o);
32822         
32823     },
32824     
32825     closable : function(closable)
32826     {
32827         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32828             
32829             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32830             
32831             if(closable){
32832                 el.show();
32833                 return;
32834             }
32835             
32836             el.hide();
32837             
32838         }, this);
32839     },
32840     
32841     xhrOnLoad : function(xhr)
32842     {
32843         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32844             el.remove();
32845         }, this);
32846         
32847         if (xhr.readyState !== 4) {
32848             this.arrange();
32849             this.fireEvent('exception', this, xhr);
32850             return;
32851         }
32852
32853         var response = Roo.decode(xhr.responseText);
32854         
32855         if(!response.success){
32856             this.arrange();
32857             this.fireEvent('exception', this, xhr);
32858             return;
32859         }
32860         
32861         var file = this.renderPreview(response.data);
32862         
32863         this.files.push(file);
32864         
32865         this.arrange();
32866         
32867         this.fireEvent('afterupload', this, xhr);
32868         
32869     },
32870     
32871     xhrOnError : function(xhr)
32872     {
32873         Roo.log('xhr on error');
32874         
32875         var response = Roo.decode(xhr.responseText);
32876           
32877         Roo.log(response);
32878         
32879         this.arrange();
32880     },
32881     
32882     process : function(file)
32883     {
32884         if(this.fireEvent('process', this, file) !== false){
32885             if(this.editable && file.type.indexOf('image') != -1){
32886                 this.fireEvent('edit', this, file);
32887                 return;
32888             }
32889
32890             this.uploadStart(file, false);
32891
32892             return;
32893         }
32894         
32895     },
32896     
32897     uploadStart : function(file, crop)
32898     {
32899         this.xhr = new XMLHttpRequest();
32900         
32901         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32902             this.arrange();
32903             return;
32904         }
32905         
32906         file.xhr = this.xhr;
32907             
32908         this.managerEl.createChild({
32909             tag : 'div',
32910             cls : 'roo-document-manager-loading',
32911             cn : [
32912                 {
32913                     tag : 'div',
32914                     tooltip : file.name,
32915                     cls : 'roo-document-manager-thumb',
32916                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32917                 }
32918             ]
32919
32920         });
32921
32922         this.xhr.open(this.method, this.url, true);
32923         
32924         var headers = {
32925             "Accept": "application/json",
32926             "Cache-Control": "no-cache",
32927             "X-Requested-With": "XMLHttpRequest"
32928         };
32929         
32930         for (var headerName in headers) {
32931             var headerValue = headers[headerName];
32932             if (headerValue) {
32933                 this.xhr.setRequestHeader(headerName, headerValue);
32934             }
32935         }
32936         
32937         var _this = this;
32938         
32939         this.xhr.onload = function()
32940         {
32941             _this.xhrOnLoad(_this.xhr);
32942         }
32943         
32944         this.xhr.onerror = function()
32945         {
32946             _this.xhrOnError(_this.xhr);
32947         }
32948         
32949         var formData = new FormData();
32950
32951         formData.append('returnHTML', 'NO');
32952         
32953         if(crop){
32954             formData.append('crop', crop);
32955         }
32956         
32957         formData.append(this.paramName, file, file.name);
32958         
32959         var options = {
32960             file : file, 
32961             manually : false
32962         };
32963         
32964         if(this.fireEvent('prepare', this, formData, options) != false){
32965             
32966             if(options.manually){
32967                 return;
32968             }
32969             
32970             this.xhr.send(formData);
32971             return;
32972         };
32973         
32974         this.uploadCancel();
32975     },
32976     
32977     uploadCancel : function()
32978     {
32979         if (this.xhr) {
32980             this.xhr.abort();
32981         }
32982         
32983         this.delegates = [];
32984         
32985         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32986             el.remove();
32987         }, this);
32988         
32989         this.arrange();
32990     },
32991     
32992     renderPreview : function(file)
32993     {
32994         if(typeof(file.target) != 'undefined' && file.target){
32995             return file;
32996         }
32997         
32998         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32999         
33000         var previewEl = this.managerEl.createChild({
33001             tag : 'div',
33002             cls : 'roo-document-manager-preview',
33003             cn : [
33004                 {
33005                     tag : 'div',
33006                     tooltip : file[this.toolTipName],
33007                     cls : 'roo-document-manager-thumb',
33008                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33009                 },
33010                 {
33011                     tag : 'button',
33012                     cls : 'close',
33013                     html : '<i class="fa fa-times-circle"></i>'
33014                 }
33015             ]
33016         });
33017
33018         var close = previewEl.select('button.close', true).first();
33019
33020         close.on('click', this.onRemove, this, file);
33021
33022         file.target = previewEl;
33023
33024         var image = previewEl.select('img', true).first();
33025         
33026         var _this = this;
33027         
33028         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33029         
33030         image.on('click', this.onClick, this, file);
33031         
33032         this.fireEvent('previewrendered', this, file);
33033         
33034         return file;
33035         
33036     },
33037     
33038     onPreviewLoad : function(file, image)
33039     {
33040         if(typeof(file.target) == 'undefined' || !file.target){
33041             return;
33042         }
33043         
33044         var width = image.dom.naturalWidth || image.dom.width;
33045         var height = image.dom.naturalHeight || image.dom.height;
33046         
33047         if(!this.previewResize) {
33048             return;
33049         }
33050         
33051         if(width > height){
33052             file.target.addClass('wide');
33053             return;
33054         }
33055         
33056         file.target.addClass('tall');
33057         return;
33058         
33059     },
33060     
33061     uploadFromSource : function(file, crop)
33062     {
33063         this.xhr = new XMLHttpRequest();
33064         
33065         this.managerEl.createChild({
33066             tag : 'div',
33067             cls : 'roo-document-manager-loading',
33068             cn : [
33069                 {
33070                     tag : 'div',
33071                     tooltip : file.name,
33072                     cls : 'roo-document-manager-thumb',
33073                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33074                 }
33075             ]
33076
33077         });
33078
33079         this.xhr.open(this.method, this.url, true);
33080         
33081         var headers = {
33082             "Accept": "application/json",
33083             "Cache-Control": "no-cache",
33084             "X-Requested-With": "XMLHttpRequest"
33085         };
33086         
33087         for (var headerName in headers) {
33088             var headerValue = headers[headerName];
33089             if (headerValue) {
33090                 this.xhr.setRequestHeader(headerName, headerValue);
33091             }
33092         }
33093         
33094         var _this = this;
33095         
33096         this.xhr.onload = function()
33097         {
33098             _this.xhrOnLoad(_this.xhr);
33099         }
33100         
33101         this.xhr.onerror = function()
33102         {
33103             _this.xhrOnError(_this.xhr);
33104         }
33105         
33106         var formData = new FormData();
33107
33108         formData.append('returnHTML', 'NO');
33109         
33110         formData.append('crop', crop);
33111         
33112         if(typeof(file.filename) != 'undefined'){
33113             formData.append('filename', file.filename);
33114         }
33115         
33116         if(typeof(file.mimetype) != 'undefined'){
33117             formData.append('mimetype', file.mimetype);
33118         }
33119         
33120         Roo.log(formData);
33121         
33122         if(this.fireEvent('prepare', this, formData) != false){
33123             this.xhr.send(formData);
33124         };
33125     }
33126 });
33127
33128 /*
33129 * Licence: LGPL
33130 */
33131
33132 /**
33133  * @class Roo.bootstrap.DocumentViewer
33134  * @extends Roo.bootstrap.Component
33135  * Bootstrap DocumentViewer class
33136  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33137  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33138  * 
33139  * @constructor
33140  * Create a new DocumentViewer
33141  * @param {Object} config The config object
33142  */
33143
33144 Roo.bootstrap.DocumentViewer = function(config){
33145     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33146     
33147     this.addEvents({
33148         /**
33149          * @event initial
33150          * Fire after initEvent
33151          * @param {Roo.bootstrap.DocumentViewer} this
33152          */
33153         "initial" : true,
33154         /**
33155          * @event click
33156          * Fire after click
33157          * @param {Roo.bootstrap.DocumentViewer} this
33158          */
33159         "click" : true,
33160         /**
33161          * @event download
33162          * Fire after download button
33163          * @param {Roo.bootstrap.DocumentViewer} this
33164          */
33165         "download" : true,
33166         /**
33167          * @event trash
33168          * Fire after trash button
33169          * @param {Roo.bootstrap.DocumentViewer} this
33170          */
33171         "trash" : true
33172         
33173     });
33174 };
33175
33176 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33177     
33178     showDownload : true,
33179     
33180     showTrash : true,
33181     
33182     getAutoCreate : function()
33183     {
33184         var cfg = {
33185             tag : 'div',
33186             cls : 'roo-document-viewer',
33187             cn : [
33188                 {
33189                     tag : 'div',
33190                     cls : 'roo-document-viewer-body',
33191                     cn : [
33192                         {
33193                             tag : 'div',
33194                             cls : 'roo-document-viewer-thumb',
33195                             cn : [
33196                                 {
33197                                     tag : 'img',
33198                                     cls : 'roo-document-viewer-image'
33199                                 }
33200                             ]
33201                         }
33202                     ]
33203                 },
33204                 {
33205                     tag : 'div',
33206                     cls : 'roo-document-viewer-footer',
33207                     cn : {
33208                         tag : 'div',
33209                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33210                         cn : [
33211                             {
33212                                 tag : 'div',
33213                                 cls : 'btn-group roo-document-viewer-download',
33214                                 cn : [
33215                                     {
33216                                         tag : 'button',
33217                                         cls : 'btn btn-default',
33218                                         html : '<i class="fa fa-download"></i>'
33219                                     }
33220                                 ]
33221                             },
33222                             {
33223                                 tag : 'div',
33224                                 cls : 'btn-group roo-document-viewer-trash',
33225                                 cn : [
33226                                     {
33227                                         tag : 'button',
33228                                         cls : 'btn btn-default',
33229                                         html : '<i class="fa fa-trash"></i>'
33230                                     }
33231                                 ]
33232                             }
33233                         ]
33234                     }
33235                 }
33236             ]
33237         };
33238         
33239         return cfg;
33240     },
33241     
33242     initEvents : function()
33243     {
33244         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33245         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33246         
33247         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33248         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33249         
33250         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33251         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33252         
33253         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33254         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33255         
33256         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33257         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33258         
33259         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33260         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33261         
33262         this.bodyEl.on('click', this.onClick, this);
33263         this.downloadBtn.on('click', this.onDownload, this);
33264         this.trashBtn.on('click', this.onTrash, this);
33265         
33266         this.downloadBtn.hide();
33267         this.trashBtn.hide();
33268         
33269         if(this.showDownload){
33270             this.downloadBtn.show();
33271         }
33272         
33273         if(this.showTrash){
33274             this.trashBtn.show();
33275         }
33276         
33277         if(!this.showDownload && !this.showTrash) {
33278             this.footerEl.hide();
33279         }
33280         
33281     },
33282     
33283     initial : function()
33284     {
33285         this.fireEvent('initial', this);
33286         
33287     },
33288     
33289     onClick : function(e)
33290     {
33291         e.preventDefault();
33292         
33293         this.fireEvent('click', this);
33294     },
33295     
33296     onDownload : function(e)
33297     {
33298         e.preventDefault();
33299         
33300         this.fireEvent('download', this);
33301     },
33302     
33303     onTrash : function(e)
33304     {
33305         e.preventDefault();
33306         
33307         this.fireEvent('trash', this);
33308     }
33309     
33310 });
33311 /*
33312  * - LGPL
33313  *
33314  * nav progress bar
33315  * 
33316  */
33317
33318 /**
33319  * @class Roo.bootstrap.NavProgressBar
33320  * @extends Roo.bootstrap.Component
33321  * Bootstrap NavProgressBar class
33322  * 
33323  * @constructor
33324  * Create a new nav progress bar
33325  * @param {Object} config The config object
33326  */
33327
33328 Roo.bootstrap.NavProgressBar = function(config){
33329     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33330
33331     this.bullets = this.bullets || [];
33332    
33333 //    Roo.bootstrap.NavProgressBar.register(this);
33334      this.addEvents({
33335         /**
33336              * @event changed
33337              * Fires when the active item changes
33338              * @param {Roo.bootstrap.NavProgressBar} this
33339              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33340              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33341          */
33342         'changed': true
33343      });
33344     
33345 };
33346
33347 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33348     
33349     bullets : [],
33350     barItems : [],
33351     
33352     getAutoCreate : function()
33353     {
33354         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33355         
33356         cfg = {
33357             tag : 'div',
33358             cls : 'roo-navigation-bar-group',
33359             cn : [
33360                 {
33361                     tag : 'div',
33362                     cls : 'roo-navigation-top-bar'
33363                 },
33364                 {
33365                     tag : 'div',
33366                     cls : 'roo-navigation-bullets-bar',
33367                     cn : [
33368                         {
33369                             tag : 'ul',
33370                             cls : 'roo-navigation-bar'
33371                         }
33372                     ]
33373                 },
33374                 
33375                 {
33376                     tag : 'div',
33377                     cls : 'roo-navigation-bottom-bar'
33378                 }
33379             ]
33380             
33381         };
33382         
33383         return cfg;
33384         
33385     },
33386     
33387     initEvents: function() 
33388     {
33389         
33390     },
33391     
33392     onRender : function(ct, position) 
33393     {
33394         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33395         
33396         if(this.bullets.length){
33397             Roo.each(this.bullets, function(b){
33398                this.addItem(b);
33399             }, this);
33400         }
33401         
33402         this.format();
33403         
33404     },
33405     
33406     addItem : function(cfg)
33407     {
33408         var item = new Roo.bootstrap.NavProgressItem(cfg);
33409         
33410         item.parentId = this.id;
33411         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33412         
33413         if(cfg.html){
33414             var top = new Roo.bootstrap.Element({
33415                 tag : 'div',
33416                 cls : 'roo-navigation-bar-text'
33417             });
33418             
33419             var bottom = new Roo.bootstrap.Element({
33420                 tag : 'div',
33421                 cls : 'roo-navigation-bar-text'
33422             });
33423             
33424             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33425             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33426             
33427             var topText = new Roo.bootstrap.Element({
33428                 tag : 'span',
33429                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33430             });
33431             
33432             var bottomText = new Roo.bootstrap.Element({
33433                 tag : 'span',
33434                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33435             });
33436             
33437             topText.onRender(top.el, null);
33438             bottomText.onRender(bottom.el, null);
33439             
33440             item.topEl = top;
33441             item.bottomEl = bottom;
33442         }
33443         
33444         this.barItems.push(item);
33445         
33446         return item;
33447     },
33448     
33449     getActive : function()
33450     {
33451         var active = false;
33452         
33453         Roo.each(this.barItems, function(v){
33454             
33455             if (!v.isActive()) {
33456                 return;
33457             }
33458             
33459             active = v;
33460             return false;
33461             
33462         });
33463         
33464         return active;
33465     },
33466     
33467     setActiveItem : function(item)
33468     {
33469         var prev = false;
33470         
33471         Roo.each(this.barItems, function(v){
33472             if (v.rid == item.rid) {
33473                 return ;
33474             }
33475             
33476             if (v.isActive()) {
33477                 v.setActive(false);
33478                 prev = v;
33479             }
33480         });
33481
33482         item.setActive(true);
33483         
33484         this.fireEvent('changed', this, item, prev);
33485     },
33486     
33487     getBarItem: function(rid)
33488     {
33489         var ret = false;
33490         
33491         Roo.each(this.barItems, function(e) {
33492             if (e.rid != rid) {
33493                 return;
33494             }
33495             
33496             ret =  e;
33497             return false;
33498         });
33499         
33500         return ret;
33501     },
33502     
33503     indexOfItem : function(item)
33504     {
33505         var index = false;
33506         
33507         Roo.each(this.barItems, function(v, i){
33508             
33509             if (v.rid != item.rid) {
33510                 return;
33511             }
33512             
33513             index = i;
33514             return false
33515         });
33516         
33517         return index;
33518     },
33519     
33520     setActiveNext : function()
33521     {
33522         var i = this.indexOfItem(this.getActive());
33523         
33524         if (i > this.barItems.length) {
33525             return;
33526         }
33527         
33528         this.setActiveItem(this.barItems[i+1]);
33529     },
33530     
33531     setActivePrev : function()
33532     {
33533         var i = this.indexOfItem(this.getActive());
33534         
33535         if (i  < 1) {
33536             return;
33537         }
33538         
33539         this.setActiveItem(this.barItems[i-1]);
33540     },
33541     
33542     format : function()
33543     {
33544         if(!this.barItems.length){
33545             return;
33546         }
33547      
33548         var width = 100 / this.barItems.length;
33549         
33550         Roo.each(this.barItems, function(i){
33551             i.el.setStyle('width', width + '%');
33552             i.topEl.el.setStyle('width', width + '%');
33553             i.bottomEl.el.setStyle('width', width + '%');
33554         }, this);
33555         
33556     }
33557     
33558 });
33559 /*
33560  * - LGPL
33561  *
33562  * Nav Progress Item
33563  * 
33564  */
33565
33566 /**
33567  * @class Roo.bootstrap.NavProgressItem
33568  * @extends Roo.bootstrap.Component
33569  * Bootstrap NavProgressItem class
33570  * @cfg {String} rid the reference id
33571  * @cfg {Boolean} active (true|false) Is item active default false
33572  * @cfg {Boolean} disabled (true|false) Is item active default false
33573  * @cfg {String} html
33574  * @cfg {String} position (top|bottom) text position default bottom
33575  * @cfg {String} icon show icon instead of number
33576  * 
33577  * @constructor
33578  * Create a new NavProgressItem
33579  * @param {Object} config The config object
33580  */
33581 Roo.bootstrap.NavProgressItem = function(config){
33582     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33583     this.addEvents({
33584         // raw events
33585         /**
33586          * @event click
33587          * The raw click event for the entire grid.
33588          * @param {Roo.bootstrap.NavProgressItem} this
33589          * @param {Roo.EventObject} e
33590          */
33591         "click" : true
33592     });
33593    
33594 };
33595
33596 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33597     
33598     rid : '',
33599     active : false,
33600     disabled : false,
33601     html : '',
33602     position : 'bottom',
33603     icon : false,
33604     
33605     getAutoCreate : function()
33606     {
33607         var iconCls = 'roo-navigation-bar-item-icon';
33608         
33609         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33610         
33611         var cfg = {
33612             tag: 'li',
33613             cls: 'roo-navigation-bar-item',
33614             cn : [
33615                 {
33616                     tag : 'i',
33617                     cls : iconCls
33618                 }
33619             ]
33620         };
33621         
33622         if(this.active){
33623             cfg.cls += ' active';
33624         }
33625         if(this.disabled){
33626             cfg.cls += ' disabled';
33627         }
33628         
33629         return cfg;
33630     },
33631     
33632     disable : function()
33633     {
33634         this.setDisabled(true);
33635     },
33636     
33637     enable : function()
33638     {
33639         this.setDisabled(false);
33640     },
33641     
33642     initEvents: function() 
33643     {
33644         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33645         
33646         this.iconEl.on('click', this.onClick, this);
33647     },
33648     
33649     onClick : function(e)
33650     {
33651         e.preventDefault();
33652         
33653         if(this.disabled){
33654             return;
33655         }
33656         
33657         if(this.fireEvent('click', this, e) === false){
33658             return;
33659         };
33660         
33661         this.parent().setActiveItem(this);
33662     },
33663     
33664     isActive: function () 
33665     {
33666         return this.active;
33667     },
33668     
33669     setActive : function(state)
33670     {
33671         if(this.active == state){
33672             return;
33673         }
33674         
33675         this.active = state;
33676         
33677         if (state) {
33678             this.el.addClass('active');
33679             return;
33680         }
33681         
33682         this.el.removeClass('active');
33683         
33684         return;
33685     },
33686     
33687     setDisabled : function(state)
33688     {
33689         if(this.disabled == state){
33690             return;
33691         }
33692         
33693         this.disabled = state;
33694         
33695         if (state) {
33696             this.el.addClass('disabled');
33697             return;
33698         }
33699         
33700         this.el.removeClass('disabled');
33701     },
33702     
33703     tooltipEl : function()
33704     {
33705         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33706     }
33707 });
33708  
33709
33710  /*
33711  * - LGPL
33712  *
33713  * FieldLabel
33714  * 
33715  */
33716
33717 /**
33718  * @class Roo.bootstrap.FieldLabel
33719  * @extends Roo.bootstrap.Component
33720  * Bootstrap FieldLabel class
33721  * @cfg {String} html contents of the element
33722  * @cfg {String} tag tag of the element default label
33723  * @cfg {String} cls class of the element
33724  * @cfg {String} target label target 
33725  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33726  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33727  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33728  * @cfg {String} iconTooltip default "This field is required"
33729  * @cfg {String} indicatorpos (left|right) default left
33730  * 
33731  * @constructor
33732  * Create a new FieldLabel
33733  * @param {Object} config The config object
33734  */
33735
33736 Roo.bootstrap.FieldLabel = function(config){
33737     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33738     
33739     this.addEvents({
33740             /**
33741              * @event invalid
33742              * Fires after the field has been marked as invalid.
33743              * @param {Roo.form.FieldLabel} this
33744              * @param {String} msg The validation message
33745              */
33746             invalid : true,
33747             /**
33748              * @event valid
33749              * Fires after the field has been validated with no errors.
33750              * @param {Roo.form.FieldLabel} this
33751              */
33752             valid : true
33753         });
33754 };
33755
33756 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33757     
33758     tag: 'label',
33759     cls: '',
33760     html: '',
33761     target: '',
33762     allowBlank : true,
33763     invalidClass : 'has-warning',
33764     validClass : 'has-success',
33765     iconTooltip : 'This field is required',
33766     indicatorpos : 'left',
33767     
33768     getAutoCreate : function(){
33769         
33770         var cls = "";
33771         if (!this.allowBlank) {
33772             cls  = "visible";
33773         }
33774         
33775         var cfg = {
33776             tag : this.tag,
33777             cls : 'roo-bootstrap-field-label ' + this.cls,
33778             for : this.target,
33779             cn : [
33780                 {
33781                     tag : 'i',
33782                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33783                     tooltip : this.iconTooltip
33784                 },
33785                 {
33786                     tag : 'span',
33787                     html : this.html
33788                 }
33789             ] 
33790         };
33791         
33792         if(this.indicatorpos == 'right'){
33793             var cfg = {
33794                 tag : this.tag,
33795                 cls : 'roo-bootstrap-field-label ' + this.cls,
33796                 for : this.target,
33797                 cn : [
33798                     {
33799                         tag : 'span',
33800                         html : this.html
33801                     },
33802                     {
33803                         tag : 'i',
33804                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33805                         tooltip : this.iconTooltip
33806                     }
33807                 ] 
33808             };
33809         }
33810         
33811         return cfg;
33812     },
33813     
33814     initEvents: function() 
33815     {
33816         Roo.bootstrap.Element.superclass.initEvents.call(this);
33817         
33818         this.indicator = this.indicatorEl();
33819         
33820         if(this.indicator){
33821             this.indicator.removeClass('visible');
33822             this.indicator.addClass('invisible');
33823         }
33824         
33825         Roo.bootstrap.FieldLabel.register(this);
33826     },
33827     
33828     indicatorEl : function()
33829     {
33830         var indicator = this.el.select('i.roo-required-indicator',true).first();
33831         
33832         if(!indicator){
33833             return false;
33834         }
33835         
33836         return indicator;
33837         
33838     },
33839     
33840     /**
33841      * Mark this field as valid
33842      */
33843     markValid : function()
33844     {
33845         if(this.indicator){
33846             this.indicator.removeClass('visible');
33847             this.indicator.addClass('invisible');
33848         }
33849         if (Roo.bootstrap.version == 3) {
33850             this.el.removeClass(this.invalidClass);
33851             this.el.addClass(this.validClass);
33852         } else {
33853             this.el.removeClass('is-invalid');
33854             this.el.addClass('is-valid');
33855         }
33856         
33857         
33858         this.fireEvent('valid', this);
33859     },
33860     
33861     /**
33862      * Mark this field as invalid
33863      * @param {String} msg The validation message
33864      */
33865     markInvalid : function(msg)
33866     {
33867         if(this.indicator){
33868             this.indicator.removeClass('invisible');
33869             this.indicator.addClass('visible');
33870         }
33871           if (Roo.bootstrap.version == 3) {
33872             this.el.removeClass(this.validClass);
33873             this.el.addClass(this.invalidClass);
33874         } else {
33875             this.el.removeClass('is-valid');
33876             this.el.addClass('is-invalid');
33877         }
33878         
33879         
33880         this.fireEvent('invalid', this, msg);
33881     }
33882     
33883    
33884 });
33885
33886 Roo.apply(Roo.bootstrap.FieldLabel, {
33887     
33888     groups: {},
33889     
33890      /**
33891     * register a FieldLabel Group
33892     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33893     */
33894     register : function(label)
33895     {
33896         if(this.groups.hasOwnProperty(label.target)){
33897             return;
33898         }
33899      
33900         this.groups[label.target] = label;
33901         
33902     },
33903     /**
33904     * fetch a FieldLabel Group based on the target
33905     * @param {string} target
33906     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33907     */
33908     get: function(target) {
33909         if (typeof(this.groups[target]) == 'undefined') {
33910             return false;
33911         }
33912         
33913         return this.groups[target] ;
33914     }
33915 });
33916
33917  
33918
33919  /*
33920  * - LGPL
33921  *
33922  * page DateSplitField.
33923  * 
33924  */
33925
33926
33927 /**
33928  * @class Roo.bootstrap.DateSplitField
33929  * @extends Roo.bootstrap.Component
33930  * Bootstrap DateSplitField class
33931  * @cfg {string} fieldLabel - the label associated
33932  * @cfg {Number} labelWidth set the width of label (0-12)
33933  * @cfg {String} labelAlign (top|left)
33934  * @cfg {Boolean} dayAllowBlank (true|false) default false
33935  * @cfg {Boolean} monthAllowBlank (true|false) default false
33936  * @cfg {Boolean} yearAllowBlank (true|false) default false
33937  * @cfg {string} dayPlaceholder 
33938  * @cfg {string} monthPlaceholder
33939  * @cfg {string} yearPlaceholder
33940  * @cfg {string} dayFormat default 'd'
33941  * @cfg {string} monthFormat default 'm'
33942  * @cfg {string} yearFormat default 'Y'
33943  * @cfg {Number} labellg set the width of label (1-12)
33944  * @cfg {Number} labelmd set the width of label (1-12)
33945  * @cfg {Number} labelsm set the width of label (1-12)
33946  * @cfg {Number} labelxs set the width of label (1-12)
33947
33948  *     
33949  * @constructor
33950  * Create a new DateSplitField
33951  * @param {Object} config The config object
33952  */
33953
33954 Roo.bootstrap.DateSplitField = function(config){
33955     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33956     
33957     this.addEvents({
33958         // raw events
33959          /**
33960          * @event years
33961          * getting the data of years
33962          * @param {Roo.bootstrap.DateSplitField} this
33963          * @param {Object} years
33964          */
33965         "years" : true,
33966         /**
33967          * @event days
33968          * getting the data of days
33969          * @param {Roo.bootstrap.DateSplitField} this
33970          * @param {Object} days
33971          */
33972         "days" : true,
33973         /**
33974          * @event invalid
33975          * Fires after the field has been marked as invalid.
33976          * @param {Roo.form.Field} this
33977          * @param {String} msg The validation message
33978          */
33979         invalid : true,
33980        /**
33981          * @event valid
33982          * Fires after the field has been validated with no errors.
33983          * @param {Roo.form.Field} this
33984          */
33985         valid : true
33986     });
33987 };
33988
33989 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33990     
33991     fieldLabel : '',
33992     labelAlign : 'top',
33993     labelWidth : 3,
33994     dayAllowBlank : false,
33995     monthAllowBlank : false,
33996     yearAllowBlank : false,
33997     dayPlaceholder : '',
33998     monthPlaceholder : '',
33999     yearPlaceholder : '',
34000     dayFormat : 'd',
34001     monthFormat : 'm',
34002     yearFormat : 'Y',
34003     isFormField : true,
34004     labellg : 0,
34005     labelmd : 0,
34006     labelsm : 0,
34007     labelxs : 0,
34008     
34009     getAutoCreate : function()
34010     {
34011         var cfg = {
34012             tag : 'div',
34013             cls : 'row roo-date-split-field-group',
34014             cn : [
34015                 {
34016                     tag : 'input',
34017                     type : 'hidden',
34018                     cls : 'form-hidden-field roo-date-split-field-group-value',
34019                     name : this.name
34020                 }
34021             ]
34022         };
34023         
34024         var labelCls = 'col-md-12';
34025         var contentCls = 'col-md-4';
34026         
34027         if(this.fieldLabel){
34028             
34029             var label = {
34030                 tag : 'div',
34031                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34032                 cn : [
34033                     {
34034                         tag : 'label',
34035                         html : this.fieldLabel
34036                     }
34037                 ]
34038             };
34039             
34040             if(this.labelAlign == 'left'){
34041             
34042                 if(this.labelWidth > 12){
34043                     label.style = "width: " + this.labelWidth + 'px';
34044                 }
34045
34046                 if(this.labelWidth < 13 && this.labelmd == 0){
34047                     this.labelmd = this.labelWidth;
34048                 }
34049
34050                 if(this.labellg > 0){
34051                     labelCls = ' col-lg-' + this.labellg;
34052                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34053                 }
34054
34055                 if(this.labelmd > 0){
34056                     labelCls = ' col-md-' + this.labelmd;
34057                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34058                 }
34059
34060                 if(this.labelsm > 0){
34061                     labelCls = ' col-sm-' + this.labelsm;
34062                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34063                 }
34064
34065                 if(this.labelxs > 0){
34066                     labelCls = ' col-xs-' + this.labelxs;
34067                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34068                 }
34069             }
34070             
34071             label.cls += ' ' + labelCls;
34072             
34073             cfg.cn.push(label);
34074         }
34075         
34076         Roo.each(['day', 'month', 'year'], function(t){
34077             cfg.cn.push({
34078                 tag : 'div',
34079                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34080             });
34081         }, this);
34082         
34083         return cfg;
34084     },
34085     
34086     inputEl: function ()
34087     {
34088         return this.el.select('.roo-date-split-field-group-value', true).first();
34089     },
34090     
34091     onRender : function(ct, position) 
34092     {
34093         var _this = this;
34094         
34095         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34096         
34097         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34098         
34099         this.dayField = new Roo.bootstrap.ComboBox({
34100             allowBlank : this.dayAllowBlank,
34101             alwaysQuery : true,
34102             displayField : 'value',
34103             editable : false,
34104             fieldLabel : '',
34105             forceSelection : true,
34106             mode : 'local',
34107             placeholder : this.dayPlaceholder,
34108             selectOnFocus : true,
34109             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34110             triggerAction : 'all',
34111             typeAhead : true,
34112             valueField : 'value',
34113             store : new Roo.data.SimpleStore({
34114                 data : (function() {    
34115                     var days = [];
34116                     _this.fireEvent('days', _this, days);
34117                     return days;
34118                 })(),
34119                 fields : [ 'value' ]
34120             }),
34121             listeners : {
34122                 select : function (_self, record, index)
34123                 {
34124                     _this.setValue(_this.getValue());
34125                 }
34126             }
34127         });
34128
34129         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34130         
34131         this.monthField = new Roo.bootstrap.MonthField({
34132             after : '<i class=\"fa fa-calendar\"></i>',
34133             allowBlank : this.monthAllowBlank,
34134             placeholder : this.monthPlaceholder,
34135             readOnly : true,
34136             listeners : {
34137                 render : function (_self)
34138                 {
34139                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34140                         e.preventDefault();
34141                         _self.focus();
34142                     });
34143                 },
34144                 select : function (_self, oldvalue, newvalue)
34145                 {
34146                     _this.setValue(_this.getValue());
34147                 }
34148             }
34149         });
34150         
34151         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34152         
34153         this.yearField = new Roo.bootstrap.ComboBox({
34154             allowBlank : this.yearAllowBlank,
34155             alwaysQuery : true,
34156             displayField : 'value',
34157             editable : false,
34158             fieldLabel : '',
34159             forceSelection : true,
34160             mode : 'local',
34161             placeholder : this.yearPlaceholder,
34162             selectOnFocus : true,
34163             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34164             triggerAction : 'all',
34165             typeAhead : true,
34166             valueField : 'value',
34167             store : new Roo.data.SimpleStore({
34168                 data : (function() {
34169                     var years = [];
34170                     _this.fireEvent('years', _this, years);
34171                     return years;
34172                 })(),
34173                 fields : [ 'value' ]
34174             }),
34175             listeners : {
34176                 select : function (_self, record, index)
34177                 {
34178                     _this.setValue(_this.getValue());
34179                 }
34180             }
34181         });
34182
34183         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34184     },
34185     
34186     setValue : function(v, format)
34187     {
34188         this.inputEl.dom.value = v;
34189         
34190         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34191         
34192         var d = Date.parseDate(v, f);
34193         
34194         if(!d){
34195             this.validate();
34196             return;
34197         }
34198         
34199         this.setDay(d.format(this.dayFormat));
34200         this.setMonth(d.format(this.monthFormat));
34201         this.setYear(d.format(this.yearFormat));
34202         
34203         this.validate();
34204         
34205         return;
34206     },
34207     
34208     setDay : function(v)
34209     {
34210         this.dayField.setValue(v);
34211         this.inputEl.dom.value = this.getValue();
34212         this.validate();
34213         return;
34214     },
34215     
34216     setMonth : function(v)
34217     {
34218         this.monthField.setValue(v, true);
34219         this.inputEl.dom.value = this.getValue();
34220         this.validate();
34221         return;
34222     },
34223     
34224     setYear : function(v)
34225     {
34226         this.yearField.setValue(v);
34227         this.inputEl.dom.value = this.getValue();
34228         this.validate();
34229         return;
34230     },
34231     
34232     getDay : function()
34233     {
34234         return this.dayField.getValue();
34235     },
34236     
34237     getMonth : function()
34238     {
34239         return this.monthField.getValue();
34240     },
34241     
34242     getYear : function()
34243     {
34244         return this.yearField.getValue();
34245     },
34246     
34247     getValue : function()
34248     {
34249         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34250         
34251         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34252         
34253         return date;
34254     },
34255     
34256     reset : function()
34257     {
34258         this.setDay('');
34259         this.setMonth('');
34260         this.setYear('');
34261         this.inputEl.dom.value = '';
34262         this.validate();
34263         return;
34264     },
34265     
34266     validate : function()
34267     {
34268         var d = this.dayField.validate();
34269         var m = this.monthField.validate();
34270         var y = this.yearField.validate();
34271         
34272         var valid = true;
34273         
34274         if(
34275                 (!this.dayAllowBlank && !d) ||
34276                 (!this.monthAllowBlank && !m) ||
34277                 (!this.yearAllowBlank && !y)
34278         ){
34279             valid = false;
34280         }
34281         
34282         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34283             return valid;
34284         }
34285         
34286         if(valid){
34287             this.markValid();
34288             return valid;
34289         }
34290         
34291         this.markInvalid();
34292         
34293         return valid;
34294     },
34295     
34296     markValid : function()
34297     {
34298         
34299         var label = this.el.select('label', true).first();
34300         var icon = this.el.select('i.fa-star', true).first();
34301
34302         if(label && icon){
34303             icon.remove();
34304         }
34305         
34306         this.fireEvent('valid', this);
34307     },
34308     
34309      /**
34310      * Mark this field as invalid
34311      * @param {String} msg The validation message
34312      */
34313     markInvalid : function(msg)
34314     {
34315         
34316         var label = this.el.select('label', true).first();
34317         var icon = this.el.select('i.fa-star', true).first();
34318
34319         if(label && !icon){
34320             this.el.select('.roo-date-split-field-label', true).createChild({
34321                 tag : 'i',
34322                 cls : 'text-danger fa fa-lg fa-star',
34323                 tooltip : 'This field is required',
34324                 style : 'margin-right:5px;'
34325             }, label, true);
34326         }
34327         
34328         this.fireEvent('invalid', this, msg);
34329     },
34330     
34331     clearInvalid : function()
34332     {
34333         var label = this.el.select('label', true).first();
34334         var icon = this.el.select('i.fa-star', true).first();
34335
34336         if(label && icon){
34337             icon.remove();
34338         }
34339         
34340         this.fireEvent('valid', this);
34341     },
34342     
34343     getName: function()
34344     {
34345         return this.name;
34346     }
34347     
34348 });
34349
34350  /**
34351  *
34352  * This is based on 
34353  * http://masonry.desandro.com
34354  *
34355  * The idea is to render all the bricks based on vertical width...
34356  *
34357  * The original code extends 'outlayer' - we might need to use that....
34358  * 
34359  */
34360
34361
34362 /**
34363  * @class Roo.bootstrap.LayoutMasonry
34364  * @extends Roo.bootstrap.Component
34365  * Bootstrap Layout Masonry class
34366  * 
34367  * @constructor
34368  * Create a new Element
34369  * @param {Object} config The config object
34370  */
34371
34372 Roo.bootstrap.LayoutMasonry = function(config){
34373     
34374     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34375     
34376     this.bricks = [];
34377     
34378     Roo.bootstrap.LayoutMasonry.register(this);
34379     
34380     this.addEvents({
34381         // raw events
34382         /**
34383          * @event layout
34384          * Fire after layout the items
34385          * @param {Roo.bootstrap.LayoutMasonry} this
34386          * @param {Roo.EventObject} e
34387          */
34388         "layout" : true
34389     });
34390     
34391 };
34392
34393 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34394     
34395     /**
34396      * @cfg {Boolean} isLayoutInstant = no animation?
34397      */   
34398     isLayoutInstant : false, // needed?
34399    
34400     /**
34401      * @cfg {Number} boxWidth  width of the columns
34402      */   
34403     boxWidth : 450,
34404     
34405       /**
34406      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34407      */   
34408     boxHeight : 0,
34409     
34410     /**
34411      * @cfg {Number} padWidth padding below box..
34412      */   
34413     padWidth : 10, 
34414     
34415     /**
34416      * @cfg {Number} gutter gutter width..
34417      */   
34418     gutter : 10,
34419     
34420      /**
34421      * @cfg {Number} maxCols maximum number of columns
34422      */   
34423     
34424     maxCols: 0,
34425     
34426     /**
34427      * @cfg {Boolean} isAutoInitial defalut true
34428      */   
34429     isAutoInitial : true, 
34430     
34431     containerWidth: 0,
34432     
34433     /**
34434      * @cfg {Boolean} isHorizontal defalut false
34435      */   
34436     isHorizontal : false, 
34437
34438     currentSize : null,
34439     
34440     tag: 'div',
34441     
34442     cls: '',
34443     
34444     bricks: null, //CompositeElement
34445     
34446     cols : 1,
34447     
34448     _isLayoutInited : false,
34449     
34450 //    isAlternative : false, // only use for vertical layout...
34451     
34452     /**
34453      * @cfg {Number} alternativePadWidth padding below box..
34454      */   
34455     alternativePadWidth : 50,
34456     
34457     selectedBrick : [],
34458     
34459     getAutoCreate : function(){
34460         
34461         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34462         
34463         var cfg = {
34464             tag: this.tag,
34465             cls: 'blog-masonary-wrapper ' + this.cls,
34466             cn : {
34467                 cls : 'mas-boxes masonary'
34468             }
34469         };
34470         
34471         return cfg;
34472     },
34473     
34474     getChildContainer: function( )
34475     {
34476         if (this.boxesEl) {
34477             return this.boxesEl;
34478         }
34479         
34480         this.boxesEl = this.el.select('.mas-boxes').first();
34481         
34482         return this.boxesEl;
34483     },
34484     
34485     
34486     initEvents : function()
34487     {
34488         var _this = this;
34489         
34490         if(this.isAutoInitial){
34491             Roo.log('hook children rendered');
34492             this.on('childrenrendered', function() {
34493                 Roo.log('children rendered');
34494                 _this.initial();
34495             } ,this);
34496         }
34497     },
34498     
34499     initial : function()
34500     {
34501         this.selectedBrick = [];
34502         
34503         this.currentSize = this.el.getBox(true);
34504         
34505         Roo.EventManager.onWindowResize(this.resize, this); 
34506
34507         if(!this.isAutoInitial){
34508             this.layout();
34509             return;
34510         }
34511         
34512         this.layout();
34513         
34514         return;
34515         //this.layout.defer(500,this);
34516         
34517     },
34518     
34519     resize : function()
34520     {
34521         var cs = this.el.getBox(true);
34522         
34523         if (
34524                 this.currentSize.width == cs.width && 
34525                 this.currentSize.x == cs.x && 
34526                 this.currentSize.height == cs.height && 
34527                 this.currentSize.y == cs.y 
34528         ) {
34529             Roo.log("no change in with or X or Y");
34530             return;
34531         }
34532         
34533         this.currentSize = cs;
34534         
34535         this.layout();
34536         
34537     },
34538     
34539     layout : function()
34540     {   
34541         this._resetLayout();
34542         
34543         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34544         
34545         this.layoutItems( isInstant );
34546       
34547         this._isLayoutInited = true;
34548         
34549         this.fireEvent('layout', this);
34550         
34551     },
34552     
34553     _resetLayout : function()
34554     {
34555         if(this.isHorizontal){
34556             this.horizontalMeasureColumns();
34557             return;
34558         }
34559         
34560         this.verticalMeasureColumns();
34561         
34562     },
34563     
34564     verticalMeasureColumns : function()
34565     {
34566         this.getContainerWidth();
34567         
34568 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34569 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34570 //            return;
34571 //        }
34572         
34573         var boxWidth = this.boxWidth + this.padWidth;
34574         
34575         if(this.containerWidth < this.boxWidth){
34576             boxWidth = this.containerWidth
34577         }
34578         
34579         var containerWidth = this.containerWidth;
34580         
34581         var cols = Math.floor(containerWidth / boxWidth);
34582         
34583         this.cols = Math.max( cols, 1 );
34584         
34585         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34586         
34587         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34588         
34589         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34590         
34591         this.colWidth = boxWidth + avail - this.padWidth;
34592         
34593         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34594         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34595     },
34596     
34597     horizontalMeasureColumns : function()
34598     {
34599         this.getContainerWidth();
34600         
34601         var boxWidth = this.boxWidth;
34602         
34603         if(this.containerWidth < boxWidth){
34604             boxWidth = this.containerWidth;
34605         }
34606         
34607         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34608         
34609         this.el.setHeight(boxWidth);
34610         
34611     },
34612     
34613     getContainerWidth : function()
34614     {
34615         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34616     },
34617     
34618     layoutItems : function( isInstant )
34619     {
34620         Roo.log(this.bricks);
34621         
34622         var items = Roo.apply([], this.bricks);
34623         
34624         if(this.isHorizontal){
34625             this._horizontalLayoutItems( items , isInstant );
34626             return;
34627         }
34628         
34629 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34630 //            this._verticalAlternativeLayoutItems( items , isInstant );
34631 //            return;
34632 //        }
34633         
34634         this._verticalLayoutItems( items , isInstant );
34635         
34636     },
34637     
34638     _verticalLayoutItems : function ( items , isInstant)
34639     {
34640         if ( !items || !items.length ) {
34641             return;
34642         }
34643         
34644         var standard = [
34645             ['xs', 'xs', 'xs', 'tall'],
34646             ['xs', 'xs', 'tall'],
34647             ['xs', 'xs', 'sm'],
34648             ['xs', 'xs', 'xs'],
34649             ['xs', 'tall'],
34650             ['xs', 'sm'],
34651             ['xs', 'xs'],
34652             ['xs'],
34653             
34654             ['sm', 'xs', 'xs'],
34655             ['sm', 'xs'],
34656             ['sm'],
34657             
34658             ['tall', 'xs', 'xs', 'xs'],
34659             ['tall', 'xs', 'xs'],
34660             ['tall', 'xs'],
34661             ['tall']
34662             
34663         ];
34664         
34665         var queue = [];
34666         
34667         var boxes = [];
34668         
34669         var box = [];
34670         
34671         Roo.each(items, function(item, k){
34672             
34673             switch (item.size) {
34674                 // these layouts take up a full box,
34675                 case 'md' :
34676                 case 'md-left' :
34677                 case 'md-right' :
34678                 case 'wide' :
34679                     
34680                     if(box.length){
34681                         boxes.push(box);
34682                         box = [];
34683                     }
34684                     
34685                     boxes.push([item]);
34686                     
34687                     break;
34688                     
34689                 case 'xs' :
34690                 case 'sm' :
34691                 case 'tall' :
34692                     
34693                     box.push(item);
34694                     
34695                     break;
34696                 default :
34697                     break;
34698                     
34699             }
34700             
34701         }, this);
34702         
34703         if(box.length){
34704             boxes.push(box);
34705             box = [];
34706         }
34707         
34708         var filterPattern = function(box, length)
34709         {
34710             if(!box.length){
34711                 return;
34712             }
34713             
34714             var match = false;
34715             
34716             var pattern = box.slice(0, length);
34717             
34718             var format = [];
34719             
34720             Roo.each(pattern, function(i){
34721                 format.push(i.size);
34722             }, this);
34723             
34724             Roo.each(standard, function(s){
34725                 
34726                 if(String(s) != String(format)){
34727                     return;
34728                 }
34729                 
34730                 match = true;
34731                 return false;
34732                 
34733             }, this);
34734             
34735             if(!match && length == 1){
34736                 return;
34737             }
34738             
34739             if(!match){
34740                 filterPattern(box, length - 1);
34741                 return;
34742             }
34743                 
34744             queue.push(pattern);
34745
34746             box = box.slice(length, box.length);
34747
34748             filterPattern(box, 4);
34749
34750             return;
34751             
34752         }
34753         
34754         Roo.each(boxes, function(box, k){
34755             
34756             if(!box.length){
34757                 return;
34758             }
34759             
34760             if(box.length == 1){
34761                 queue.push(box);
34762                 return;
34763             }
34764             
34765             filterPattern(box, 4);
34766             
34767         }, this);
34768         
34769         this._processVerticalLayoutQueue( queue, isInstant );
34770         
34771     },
34772     
34773 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34774 //    {
34775 //        if ( !items || !items.length ) {
34776 //            return;
34777 //        }
34778 //
34779 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34780 //        
34781 //    },
34782     
34783     _horizontalLayoutItems : function ( items , isInstant)
34784     {
34785         if ( !items || !items.length || items.length < 3) {
34786             return;
34787         }
34788         
34789         items.reverse();
34790         
34791         var eItems = items.slice(0, 3);
34792         
34793         items = items.slice(3, items.length);
34794         
34795         var standard = [
34796             ['xs', 'xs', 'xs', 'wide'],
34797             ['xs', 'xs', 'wide'],
34798             ['xs', 'xs', 'sm'],
34799             ['xs', 'xs', 'xs'],
34800             ['xs', 'wide'],
34801             ['xs', 'sm'],
34802             ['xs', 'xs'],
34803             ['xs'],
34804             
34805             ['sm', 'xs', 'xs'],
34806             ['sm', 'xs'],
34807             ['sm'],
34808             
34809             ['wide', 'xs', 'xs', 'xs'],
34810             ['wide', 'xs', 'xs'],
34811             ['wide', 'xs'],
34812             ['wide'],
34813             
34814             ['wide-thin']
34815         ];
34816         
34817         var queue = [];
34818         
34819         var boxes = [];
34820         
34821         var box = [];
34822         
34823         Roo.each(items, function(item, k){
34824             
34825             switch (item.size) {
34826                 case 'md' :
34827                 case 'md-left' :
34828                 case 'md-right' :
34829                 case 'tall' :
34830                     
34831                     if(box.length){
34832                         boxes.push(box);
34833                         box = [];
34834                     }
34835                     
34836                     boxes.push([item]);
34837                     
34838                     break;
34839                     
34840                 case 'xs' :
34841                 case 'sm' :
34842                 case 'wide' :
34843                 case 'wide-thin' :
34844                     
34845                     box.push(item);
34846                     
34847                     break;
34848                 default :
34849                     break;
34850                     
34851             }
34852             
34853         }, this);
34854         
34855         if(box.length){
34856             boxes.push(box);
34857             box = [];
34858         }
34859         
34860         var filterPattern = function(box, length)
34861         {
34862             if(!box.length){
34863                 return;
34864             }
34865             
34866             var match = false;
34867             
34868             var pattern = box.slice(0, length);
34869             
34870             var format = [];
34871             
34872             Roo.each(pattern, function(i){
34873                 format.push(i.size);
34874             }, this);
34875             
34876             Roo.each(standard, function(s){
34877                 
34878                 if(String(s) != String(format)){
34879                     return;
34880                 }
34881                 
34882                 match = true;
34883                 return false;
34884                 
34885             }, this);
34886             
34887             if(!match && length == 1){
34888                 return;
34889             }
34890             
34891             if(!match){
34892                 filterPattern(box, length - 1);
34893                 return;
34894             }
34895                 
34896             queue.push(pattern);
34897
34898             box = box.slice(length, box.length);
34899
34900             filterPattern(box, 4);
34901
34902             return;
34903             
34904         }
34905         
34906         Roo.each(boxes, function(box, k){
34907             
34908             if(!box.length){
34909                 return;
34910             }
34911             
34912             if(box.length == 1){
34913                 queue.push(box);
34914                 return;
34915             }
34916             
34917             filterPattern(box, 4);
34918             
34919         }, this);
34920         
34921         
34922         var prune = [];
34923         
34924         var pos = this.el.getBox(true);
34925         
34926         var minX = pos.x;
34927         
34928         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34929         
34930         var hit_end = false;
34931         
34932         Roo.each(queue, function(box){
34933             
34934             if(hit_end){
34935                 
34936                 Roo.each(box, function(b){
34937                 
34938                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34939                     b.el.hide();
34940
34941                 }, this);
34942
34943                 return;
34944             }
34945             
34946             var mx = 0;
34947             
34948             Roo.each(box, function(b){
34949                 
34950                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34951                 b.el.show();
34952
34953                 mx = Math.max(mx, b.x);
34954                 
34955             }, this);
34956             
34957             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34958             
34959             if(maxX < minX){
34960                 
34961                 Roo.each(box, function(b){
34962                 
34963                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34964                     b.el.hide();
34965                     
34966                 }, this);
34967                 
34968                 hit_end = true;
34969                 
34970                 return;
34971             }
34972             
34973             prune.push(box);
34974             
34975         }, this);
34976         
34977         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34978     },
34979     
34980     /** Sets position of item in DOM
34981     * @param {Element} item
34982     * @param {Number} x - horizontal position
34983     * @param {Number} y - vertical position
34984     * @param {Boolean} isInstant - disables transitions
34985     */
34986     _processVerticalLayoutQueue : function( queue, isInstant )
34987     {
34988         var pos = this.el.getBox(true);
34989         var x = pos.x;
34990         var y = pos.y;
34991         var maxY = [];
34992         
34993         for (var i = 0; i < this.cols; i++){
34994             maxY[i] = pos.y;
34995         }
34996         
34997         Roo.each(queue, function(box, k){
34998             
34999             var col = k % this.cols;
35000             
35001             Roo.each(box, function(b,kk){
35002                 
35003                 b.el.position('absolute');
35004                 
35005                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35006                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35007                 
35008                 if(b.size == 'md-left' || b.size == 'md-right'){
35009                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35010                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35011                 }
35012                 
35013                 b.el.setWidth(width);
35014                 b.el.setHeight(height);
35015                 // iframe?
35016                 b.el.select('iframe',true).setSize(width,height);
35017                 
35018             }, this);
35019             
35020             for (var i = 0; i < this.cols; i++){
35021                 
35022                 if(maxY[i] < maxY[col]){
35023                     col = i;
35024                     continue;
35025                 }
35026                 
35027                 col = Math.min(col, i);
35028                 
35029             }
35030             
35031             x = pos.x + col * (this.colWidth + this.padWidth);
35032             
35033             y = maxY[col];
35034             
35035             var positions = [];
35036             
35037             switch (box.length){
35038                 case 1 :
35039                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35040                     break;
35041                 case 2 :
35042                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35043                     break;
35044                 case 3 :
35045                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35046                     break;
35047                 case 4 :
35048                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35049                     break;
35050                 default :
35051                     break;
35052             }
35053             
35054             Roo.each(box, function(b,kk){
35055                 
35056                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35057                 
35058                 var sz = b.el.getSize();
35059                 
35060                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35061                 
35062             }, this);
35063             
35064         }, this);
35065         
35066         var mY = 0;
35067         
35068         for (var i = 0; i < this.cols; i++){
35069             mY = Math.max(mY, maxY[i]);
35070         }
35071         
35072         this.el.setHeight(mY - pos.y);
35073         
35074     },
35075     
35076 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35077 //    {
35078 //        var pos = this.el.getBox(true);
35079 //        var x = pos.x;
35080 //        var y = pos.y;
35081 //        var maxX = pos.right;
35082 //        
35083 //        var maxHeight = 0;
35084 //        
35085 //        Roo.each(items, function(item, k){
35086 //            
35087 //            var c = k % 2;
35088 //            
35089 //            item.el.position('absolute');
35090 //                
35091 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35092 //
35093 //            item.el.setWidth(width);
35094 //
35095 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35096 //
35097 //            item.el.setHeight(height);
35098 //            
35099 //            if(c == 0){
35100 //                item.el.setXY([x, y], isInstant ? false : true);
35101 //            } else {
35102 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35103 //            }
35104 //            
35105 //            y = y + height + this.alternativePadWidth;
35106 //            
35107 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35108 //            
35109 //        }, this);
35110 //        
35111 //        this.el.setHeight(maxHeight);
35112 //        
35113 //    },
35114     
35115     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35116     {
35117         var pos = this.el.getBox(true);
35118         
35119         var minX = pos.x;
35120         var minY = pos.y;
35121         
35122         var maxX = pos.right;
35123         
35124         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35125         
35126         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35127         
35128         Roo.each(queue, function(box, k){
35129             
35130             Roo.each(box, function(b, kk){
35131                 
35132                 b.el.position('absolute');
35133                 
35134                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35135                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35136                 
35137                 if(b.size == 'md-left' || b.size == 'md-right'){
35138                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35139                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35140                 }
35141                 
35142                 b.el.setWidth(width);
35143                 b.el.setHeight(height);
35144                 
35145             }, this);
35146             
35147             if(!box.length){
35148                 return;
35149             }
35150             
35151             var positions = [];
35152             
35153             switch (box.length){
35154                 case 1 :
35155                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35156                     break;
35157                 case 2 :
35158                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35159                     break;
35160                 case 3 :
35161                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35162                     break;
35163                 case 4 :
35164                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35165                     break;
35166                 default :
35167                     break;
35168             }
35169             
35170             Roo.each(box, function(b,kk){
35171                 
35172                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35173                 
35174                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35175                 
35176             }, this);
35177             
35178         }, this);
35179         
35180     },
35181     
35182     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35183     {
35184         Roo.each(eItems, function(b,k){
35185             
35186             b.size = (k == 0) ? 'sm' : 'xs';
35187             b.x = (k == 0) ? 2 : 1;
35188             b.y = (k == 0) ? 2 : 1;
35189             
35190             b.el.position('absolute');
35191             
35192             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35193                 
35194             b.el.setWidth(width);
35195             
35196             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35197             
35198             b.el.setHeight(height);
35199             
35200         }, this);
35201
35202         var positions = [];
35203         
35204         positions.push({
35205             x : maxX - this.unitWidth * 2 - this.gutter,
35206             y : minY
35207         });
35208         
35209         positions.push({
35210             x : maxX - this.unitWidth,
35211             y : minY + (this.unitWidth + this.gutter) * 2
35212         });
35213         
35214         positions.push({
35215             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35216             y : minY
35217         });
35218         
35219         Roo.each(eItems, function(b,k){
35220             
35221             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35222
35223         }, this);
35224         
35225     },
35226     
35227     getVerticalOneBoxColPositions : function(x, y, box)
35228     {
35229         var pos = [];
35230         
35231         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35232         
35233         if(box[0].size == 'md-left'){
35234             rand = 0;
35235         }
35236         
35237         if(box[0].size == 'md-right'){
35238             rand = 1;
35239         }
35240         
35241         pos.push({
35242             x : x + (this.unitWidth + this.gutter) * rand,
35243             y : y
35244         });
35245         
35246         return pos;
35247     },
35248     
35249     getVerticalTwoBoxColPositions : function(x, y, box)
35250     {
35251         var pos = [];
35252         
35253         if(box[0].size == 'xs'){
35254             
35255             pos.push({
35256                 x : x,
35257                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35258             });
35259
35260             pos.push({
35261                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35262                 y : y
35263             });
35264             
35265             return pos;
35266             
35267         }
35268         
35269         pos.push({
35270             x : x,
35271             y : y
35272         });
35273
35274         pos.push({
35275             x : x + (this.unitWidth + this.gutter) * 2,
35276             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35277         });
35278         
35279         return pos;
35280         
35281     },
35282     
35283     getVerticalThreeBoxColPositions : function(x, y, box)
35284     {
35285         var pos = [];
35286         
35287         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35288             
35289             pos.push({
35290                 x : x,
35291                 y : y
35292             });
35293
35294             pos.push({
35295                 x : x + (this.unitWidth + this.gutter) * 1,
35296                 y : y
35297             });
35298             
35299             pos.push({
35300                 x : x + (this.unitWidth + this.gutter) * 2,
35301                 y : y
35302             });
35303             
35304             return pos;
35305             
35306         }
35307         
35308         if(box[0].size == 'xs' && box[1].size == 'xs'){
35309             
35310             pos.push({
35311                 x : x,
35312                 y : y
35313             });
35314
35315             pos.push({
35316                 x : x,
35317                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35318             });
35319             
35320             pos.push({
35321                 x : x + (this.unitWidth + this.gutter) * 1,
35322                 y : y
35323             });
35324             
35325             return pos;
35326             
35327         }
35328         
35329         pos.push({
35330             x : x,
35331             y : y
35332         });
35333
35334         pos.push({
35335             x : x + (this.unitWidth + this.gutter) * 2,
35336             y : y
35337         });
35338
35339         pos.push({
35340             x : x + (this.unitWidth + this.gutter) * 2,
35341             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35342         });
35343             
35344         return pos;
35345         
35346     },
35347     
35348     getVerticalFourBoxColPositions : function(x, y, box)
35349     {
35350         var pos = [];
35351         
35352         if(box[0].size == 'xs'){
35353             
35354             pos.push({
35355                 x : x,
35356                 y : y
35357             });
35358
35359             pos.push({
35360                 x : x,
35361                 y : y + (this.unitHeight + this.gutter) * 1
35362             });
35363             
35364             pos.push({
35365                 x : x,
35366                 y : y + (this.unitHeight + this.gutter) * 2
35367             });
35368             
35369             pos.push({
35370                 x : x + (this.unitWidth + this.gutter) * 1,
35371                 y : y
35372             });
35373             
35374             return pos;
35375             
35376         }
35377         
35378         pos.push({
35379             x : x,
35380             y : y
35381         });
35382
35383         pos.push({
35384             x : x + (this.unitWidth + this.gutter) * 2,
35385             y : y
35386         });
35387
35388         pos.push({
35389             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35390             y : y + (this.unitHeight + this.gutter) * 1
35391         });
35392
35393         pos.push({
35394             x : x + (this.unitWidth + this.gutter) * 2,
35395             y : y + (this.unitWidth + this.gutter) * 2
35396         });
35397
35398         return pos;
35399         
35400     },
35401     
35402     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35403     {
35404         var pos = [];
35405         
35406         if(box[0].size == 'md-left'){
35407             pos.push({
35408                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35409                 y : minY
35410             });
35411             
35412             return pos;
35413         }
35414         
35415         if(box[0].size == 'md-right'){
35416             pos.push({
35417                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35418                 y : minY + (this.unitWidth + this.gutter) * 1
35419             });
35420             
35421             return pos;
35422         }
35423         
35424         var rand = Math.floor(Math.random() * (4 - box[0].y));
35425         
35426         pos.push({
35427             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35428             y : minY + (this.unitWidth + this.gutter) * rand
35429         });
35430         
35431         return pos;
35432         
35433     },
35434     
35435     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35436     {
35437         var pos = [];
35438         
35439         if(box[0].size == 'xs'){
35440             
35441             pos.push({
35442                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35443                 y : minY
35444             });
35445
35446             pos.push({
35447                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35448                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35449             });
35450             
35451             return pos;
35452             
35453         }
35454         
35455         pos.push({
35456             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35457             y : minY
35458         });
35459
35460         pos.push({
35461             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35462             y : minY + (this.unitWidth + this.gutter) * 2
35463         });
35464         
35465         return pos;
35466         
35467     },
35468     
35469     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35470     {
35471         var pos = [];
35472         
35473         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35474             
35475             pos.push({
35476                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35477                 y : minY
35478             });
35479
35480             pos.push({
35481                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35482                 y : minY + (this.unitWidth + this.gutter) * 1
35483             });
35484             
35485             pos.push({
35486                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35487                 y : minY + (this.unitWidth + this.gutter) * 2
35488             });
35489             
35490             return pos;
35491             
35492         }
35493         
35494         if(box[0].size == 'xs' && box[1].size == 'xs'){
35495             
35496             pos.push({
35497                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35498                 y : minY
35499             });
35500
35501             pos.push({
35502                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35503                 y : minY
35504             });
35505             
35506             pos.push({
35507                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35508                 y : minY + (this.unitWidth + this.gutter) * 1
35509             });
35510             
35511             return pos;
35512             
35513         }
35514         
35515         pos.push({
35516             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35517             y : minY
35518         });
35519
35520         pos.push({
35521             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35522             y : minY + (this.unitWidth + this.gutter) * 2
35523         });
35524
35525         pos.push({
35526             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35527             y : minY + (this.unitWidth + this.gutter) * 2
35528         });
35529             
35530         return pos;
35531         
35532     },
35533     
35534     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35535     {
35536         var pos = [];
35537         
35538         if(box[0].size == 'xs'){
35539             
35540             pos.push({
35541                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35542                 y : minY
35543             });
35544
35545             pos.push({
35546                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35547                 y : minY
35548             });
35549             
35550             pos.push({
35551                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35552                 y : minY
35553             });
35554             
35555             pos.push({
35556                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35557                 y : minY + (this.unitWidth + this.gutter) * 1
35558             });
35559             
35560             return pos;
35561             
35562         }
35563         
35564         pos.push({
35565             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35566             y : minY
35567         });
35568         
35569         pos.push({
35570             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35571             y : minY + (this.unitWidth + this.gutter) * 2
35572         });
35573         
35574         pos.push({
35575             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35576             y : minY + (this.unitWidth + this.gutter) * 2
35577         });
35578         
35579         pos.push({
35580             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35581             y : minY + (this.unitWidth + this.gutter) * 2
35582         });
35583
35584         return pos;
35585         
35586     },
35587     
35588     /**
35589     * remove a Masonry Brick
35590     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35591     */
35592     removeBrick : function(brick_id)
35593     {
35594         if (!brick_id) {
35595             return;
35596         }
35597         
35598         for (var i = 0; i<this.bricks.length; i++) {
35599             if (this.bricks[i].id == brick_id) {
35600                 this.bricks.splice(i,1);
35601                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35602                 this.initial();
35603             }
35604         }
35605     },
35606     
35607     /**
35608     * adds a Masonry Brick
35609     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35610     */
35611     addBrick : function(cfg)
35612     {
35613         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35614         //this.register(cn);
35615         cn.parentId = this.id;
35616         cn.render(this.el);
35617         return cn;
35618     },
35619     
35620     /**
35621     * register a Masonry Brick
35622     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35623     */
35624     
35625     register : function(brick)
35626     {
35627         this.bricks.push(brick);
35628         brick.masonryId = this.id;
35629     },
35630     
35631     /**
35632     * clear all the Masonry Brick
35633     */
35634     clearAll : function()
35635     {
35636         this.bricks = [];
35637         //this.getChildContainer().dom.innerHTML = "";
35638         this.el.dom.innerHTML = '';
35639     },
35640     
35641     getSelected : function()
35642     {
35643         if (!this.selectedBrick) {
35644             return false;
35645         }
35646         
35647         return this.selectedBrick;
35648     }
35649 });
35650
35651 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35652     
35653     groups: {},
35654      /**
35655     * register a Masonry Layout
35656     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35657     */
35658     
35659     register : function(layout)
35660     {
35661         this.groups[layout.id] = layout;
35662     },
35663     /**
35664     * fetch a  Masonry Layout based on the masonry layout ID
35665     * @param {string} the masonry layout to add
35666     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35667     */
35668     
35669     get: function(layout_id) {
35670         if (typeof(this.groups[layout_id]) == 'undefined') {
35671             return false;
35672         }
35673         return this.groups[layout_id] ;
35674     }
35675     
35676     
35677     
35678 });
35679
35680  
35681
35682  /**
35683  *
35684  * This is based on 
35685  * http://masonry.desandro.com
35686  *
35687  * The idea is to render all the bricks based on vertical width...
35688  *
35689  * The original code extends 'outlayer' - we might need to use that....
35690  * 
35691  */
35692
35693
35694 /**
35695  * @class Roo.bootstrap.LayoutMasonryAuto
35696  * @extends Roo.bootstrap.Component
35697  * Bootstrap Layout Masonry class
35698  * 
35699  * @constructor
35700  * Create a new Element
35701  * @param {Object} config The config object
35702  */
35703
35704 Roo.bootstrap.LayoutMasonryAuto = function(config){
35705     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35706 };
35707
35708 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35709     
35710       /**
35711      * @cfg {Boolean} isFitWidth  - resize the width..
35712      */   
35713     isFitWidth : false,  // options..
35714     /**
35715      * @cfg {Boolean} isOriginLeft = left align?
35716      */   
35717     isOriginLeft : true,
35718     /**
35719      * @cfg {Boolean} isOriginTop = top align?
35720      */   
35721     isOriginTop : false,
35722     /**
35723      * @cfg {Boolean} isLayoutInstant = no animation?
35724      */   
35725     isLayoutInstant : false, // needed?
35726     /**
35727      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35728      */   
35729     isResizingContainer : true,
35730     /**
35731      * @cfg {Number} columnWidth  width of the columns 
35732      */   
35733     
35734     columnWidth : 0,
35735     
35736     /**
35737      * @cfg {Number} maxCols maximum number of columns
35738      */   
35739     
35740     maxCols: 0,
35741     /**
35742      * @cfg {Number} padHeight padding below box..
35743      */   
35744     
35745     padHeight : 10, 
35746     
35747     /**
35748      * @cfg {Boolean} isAutoInitial defalut true
35749      */   
35750     
35751     isAutoInitial : true, 
35752     
35753     // private?
35754     gutter : 0,
35755     
35756     containerWidth: 0,
35757     initialColumnWidth : 0,
35758     currentSize : null,
35759     
35760     colYs : null, // array.
35761     maxY : 0,
35762     padWidth: 10,
35763     
35764     
35765     tag: 'div',
35766     cls: '',
35767     bricks: null, //CompositeElement
35768     cols : 0, // array?
35769     // element : null, // wrapped now this.el
35770     _isLayoutInited : null, 
35771     
35772     
35773     getAutoCreate : function(){
35774         
35775         var cfg = {
35776             tag: this.tag,
35777             cls: 'blog-masonary-wrapper ' + this.cls,
35778             cn : {
35779                 cls : 'mas-boxes masonary'
35780             }
35781         };
35782         
35783         return cfg;
35784     },
35785     
35786     getChildContainer: function( )
35787     {
35788         if (this.boxesEl) {
35789             return this.boxesEl;
35790         }
35791         
35792         this.boxesEl = this.el.select('.mas-boxes').first();
35793         
35794         return this.boxesEl;
35795     },
35796     
35797     
35798     initEvents : function()
35799     {
35800         var _this = this;
35801         
35802         if(this.isAutoInitial){
35803             Roo.log('hook children rendered');
35804             this.on('childrenrendered', function() {
35805                 Roo.log('children rendered');
35806                 _this.initial();
35807             } ,this);
35808         }
35809         
35810     },
35811     
35812     initial : function()
35813     {
35814         this.reloadItems();
35815
35816         this.currentSize = this.el.getBox(true);
35817
35818         /// was window resize... - let's see if this works..
35819         Roo.EventManager.onWindowResize(this.resize, this); 
35820
35821         if(!this.isAutoInitial){
35822             this.layout();
35823             return;
35824         }
35825         
35826         this.layout.defer(500,this);
35827     },
35828     
35829     reloadItems: function()
35830     {
35831         this.bricks = this.el.select('.masonry-brick', true);
35832         
35833         this.bricks.each(function(b) {
35834             //Roo.log(b.getSize());
35835             if (!b.attr('originalwidth')) {
35836                 b.attr('originalwidth',  b.getSize().width);
35837             }
35838             
35839         });
35840         
35841         Roo.log(this.bricks.elements.length);
35842     },
35843     
35844     resize : function()
35845     {
35846         Roo.log('resize');
35847         var cs = this.el.getBox(true);
35848         
35849         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35850             Roo.log("no change in with or X");
35851             return;
35852         }
35853         this.currentSize = cs;
35854         this.layout();
35855     },
35856     
35857     layout : function()
35858     {
35859          Roo.log('layout');
35860         this._resetLayout();
35861         //this._manageStamps();
35862       
35863         // don't animate first layout
35864         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35865         this.layoutItems( isInstant );
35866       
35867         // flag for initalized
35868         this._isLayoutInited = true;
35869     },
35870     
35871     layoutItems : function( isInstant )
35872     {
35873         //var items = this._getItemsForLayout( this.items );
35874         // original code supports filtering layout items.. we just ignore it..
35875         
35876         this._layoutItems( this.bricks , isInstant );
35877       
35878         this._postLayout();
35879     },
35880     _layoutItems : function ( items , isInstant)
35881     {
35882        //this.fireEvent( 'layout', this, items );
35883     
35884
35885         if ( !items || !items.elements.length ) {
35886           // no items, emit event with empty array
35887             return;
35888         }
35889
35890         var queue = [];
35891         items.each(function(item) {
35892             Roo.log("layout item");
35893             Roo.log(item);
35894             // get x/y object from method
35895             var position = this._getItemLayoutPosition( item );
35896             // enqueue
35897             position.item = item;
35898             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35899             queue.push( position );
35900         }, this);
35901       
35902         this._processLayoutQueue( queue );
35903     },
35904     /** Sets position of item in DOM
35905     * @param {Element} item
35906     * @param {Number} x - horizontal position
35907     * @param {Number} y - vertical position
35908     * @param {Boolean} isInstant - disables transitions
35909     */
35910     _processLayoutQueue : function( queue )
35911     {
35912         for ( var i=0, len = queue.length; i < len; i++ ) {
35913             var obj = queue[i];
35914             obj.item.position('absolute');
35915             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35916         }
35917     },
35918       
35919     
35920     /**
35921     * Any logic you want to do after each layout,
35922     * i.e. size the container
35923     */
35924     _postLayout : function()
35925     {
35926         this.resizeContainer();
35927     },
35928     
35929     resizeContainer : function()
35930     {
35931         if ( !this.isResizingContainer ) {
35932             return;
35933         }
35934         var size = this._getContainerSize();
35935         if ( size ) {
35936             this.el.setSize(size.width,size.height);
35937             this.boxesEl.setSize(size.width,size.height);
35938         }
35939     },
35940     
35941     
35942     
35943     _resetLayout : function()
35944     {
35945         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35946         this.colWidth = this.el.getWidth();
35947         //this.gutter = this.el.getWidth(); 
35948         
35949         this.measureColumns();
35950
35951         // reset column Y
35952         var i = this.cols;
35953         this.colYs = [];
35954         while (i--) {
35955             this.colYs.push( 0 );
35956         }
35957     
35958         this.maxY = 0;
35959     },
35960
35961     measureColumns : function()
35962     {
35963         this.getContainerWidth();
35964       // if columnWidth is 0, default to outerWidth of first item
35965         if ( !this.columnWidth ) {
35966             var firstItem = this.bricks.first();
35967             Roo.log(firstItem);
35968             this.columnWidth  = this.containerWidth;
35969             if (firstItem && firstItem.attr('originalwidth') ) {
35970                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35971             }
35972             // columnWidth fall back to item of first element
35973             Roo.log("set column width?");
35974                         this.initialColumnWidth = this.columnWidth  ;
35975
35976             // if first elem has no width, default to size of container
35977             
35978         }
35979         
35980         
35981         if (this.initialColumnWidth) {
35982             this.columnWidth = this.initialColumnWidth;
35983         }
35984         
35985         
35986             
35987         // column width is fixed at the top - however if container width get's smaller we should
35988         // reduce it...
35989         
35990         // this bit calcs how man columns..
35991             
35992         var columnWidth = this.columnWidth += this.gutter;
35993       
35994         // calculate columns
35995         var containerWidth = this.containerWidth + this.gutter;
35996         
35997         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35998         // fix rounding errors, typically with gutters
35999         var excess = columnWidth - containerWidth % columnWidth;
36000         
36001         
36002         // if overshoot is less than a pixel, round up, otherwise floor it
36003         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36004         cols = Math[ mathMethod ]( cols );
36005         this.cols = Math.max( cols, 1 );
36006         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36007         
36008          // padding positioning..
36009         var totalColWidth = this.cols * this.columnWidth;
36010         var padavail = this.containerWidth - totalColWidth;
36011         // so for 2 columns - we need 3 'pads'
36012         
36013         var padNeeded = (1+this.cols) * this.padWidth;
36014         
36015         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36016         
36017         this.columnWidth += padExtra
36018         //this.padWidth = Math.floor(padavail /  ( this.cols));
36019         
36020         // adjust colum width so that padding is fixed??
36021         
36022         // we have 3 columns ... total = width * 3
36023         // we have X left over... that should be used by 
36024         
36025         //if (this.expandC) {
36026             
36027         //}
36028         
36029         
36030         
36031     },
36032     
36033     getContainerWidth : function()
36034     {
36035        /* // container is parent if fit width
36036         var container = this.isFitWidth ? this.element.parentNode : this.element;
36037         // check that this.size and size are there
36038         // IE8 triggers resize on body size change, so they might not be
36039         
36040         var size = getSize( container );  //FIXME
36041         this.containerWidth = size && size.innerWidth; //FIXME
36042         */
36043          
36044         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36045         
36046     },
36047     
36048     _getItemLayoutPosition : function( item )  // what is item?
36049     {
36050         // we resize the item to our columnWidth..
36051       
36052         item.setWidth(this.columnWidth);
36053         item.autoBoxAdjust  = false;
36054         
36055         var sz = item.getSize();
36056  
36057         // how many columns does this brick span
36058         var remainder = this.containerWidth % this.columnWidth;
36059         
36060         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36061         // round if off by 1 pixel, otherwise use ceil
36062         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36063         colSpan = Math.min( colSpan, this.cols );
36064         
36065         // normally this should be '1' as we dont' currently allow multi width columns..
36066         
36067         var colGroup = this._getColGroup( colSpan );
36068         // get the minimum Y value from the columns
36069         var minimumY = Math.min.apply( Math, colGroup );
36070         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36071         
36072         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36073          
36074         // position the brick
36075         var position = {
36076             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36077             y: this.currentSize.y + minimumY + this.padHeight
36078         };
36079         
36080         Roo.log(position);
36081         // apply setHeight to necessary columns
36082         var setHeight = minimumY + sz.height + this.padHeight;
36083         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36084         
36085         var setSpan = this.cols + 1 - colGroup.length;
36086         for ( var i = 0; i < setSpan; i++ ) {
36087           this.colYs[ shortColIndex + i ] = setHeight ;
36088         }
36089       
36090         return position;
36091     },
36092     
36093     /**
36094      * @param {Number} colSpan - number of columns the element spans
36095      * @returns {Array} colGroup
36096      */
36097     _getColGroup : function( colSpan )
36098     {
36099         if ( colSpan < 2 ) {
36100           // if brick spans only one column, use all the column Ys
36101           return this.colYs;
36102         }
36103       
36104         var colGroup = [];
36105         // how many different places could this brick fit horizontally
36106         var groupCount = this.cols + 1 - colSpan;
36107         // for each group potential horizontal position
36108         for ( var i = 0; i < groupCount; i++ ) {
36109           // make an array of colY values for that one group
36110           var groupColYs = this.colYs.slice( i, i + colSpan );
36111           // and get the max value of the array
36112           colGroup[i] = Math.max.apply( Math, groupColYs );
36113         }
36114         return colGroup;
36115     },
36116     /*
36117     _manageStamp : function( stamp )
36118     {
36119         var stampSize =  stamp.getSize();
36120         var offset = stamp.getBox();
36121         // get the columns that this stamp affects
36122         var firstX = this.isOriginLeft ? offset.x : offset.right;
36123         var lastX = firstX + stampSize.width;
36124         var firstCol = Math.floor( firstX / this.columnWidth );
36125         firstCol = Math.max( 0, firstCol );
36126         
36127         var lastCol = Math.floor( lastX / this.columnWidth );
36128         // lastCol should not go over if multiple of columnWidth #425
36129         lastCol -= lastX % this.columnWidth ? 0 : 1;
36130         lastCol = Math.min( this.cols - 1, lastCol );
36131         
36132         // set colYs to bottom of the stamp
36133         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36134             stampSize.height;
36135             
36136         for ( var i = firstCol; i <= lastCol; i++ ) {
36137           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36138         }
36139     },
36140     */
36141     
36142     _getContainerSize : function()
36143     {
36144         this.maxY = Math.max.apply( Math, this.colYs );
36145         var size = {
36146             height: this.maxY
36147         };
36148       
36149         if ( this.isFitWidth ) {
36150             size.width = this._getContainerFitWidth();
36151         }
36152       
36153         return size;
36154     },
36155     
36156     _getContainerFitWidth : function()
36157     {
36158         var unusedCols = 0;
36159         // count unused columns
36160         var i = this.cols;
36161         while ( --i ) {
36162           if ( this.colYs[i] !== 0 ) {
36163             break;
36164           }
36165           unusedCols++;
36166         }
36167         // fit container to columns that have been used
36168         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36169     },
36170     
36171     needsResizeLayout : function()
36172     {
36173         var previousWidth = this.containerWidth;
36174         this.getContainerWidth();
36175         return previousWidth !== this.containerWidth;
36176     }
36177  
36178 });
36179
36180  
36181
36182  /*
36183  * - LGPL
36184  *
36185  * element
36186  * 
36187  */
36188
36189 /**
36190  * @class Roo.bootstrap.MasonryBrick
36191  * @extends Roo.bootstrap.Component
36192  * Bootstrap MasonryBrick class
36193  * 
36194  * @constructor
36195  * Create a new MasonryBrick
36196  * @param {Object} config The config object
36197  */
36198
36199 Roo.bootstrap.MasonryBrick = function(config){
36200     
36201     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36202     
36203     Roo.bootstrap.MasonryBrick.register(this);
36204     
36205     this.addEvents({
36206         // raw events
36207         /**
36208          * @event click
36209          * When a MasonryBrick is clcik
36210          * @param {Roo.bootstrap.MasonryBrick} this
36211          * @param {Roo.EventObject} e
36212          */
36213         "click" : true
36214     });
36215 };
36216
36217 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36218     
36219     /**
36220      * @cfg {String} title
36221      */   
36222     title : '',
36223     /**
36224      * @cfg {String} html
36225      */   
36226     html : '',
36227     /**
36228      * @cfg {String} bgimage
36229      */   
36230     bgimage : '',
36231     /**
36232      * @cfg {String} videourl
36233      */   
36234     videourl : '',
36235     /**
36236      * @cfg {String} cls
36237      */   
36238     cls : '',
36239     /**
36240      * @cfg {String} href
36241      */   
36242     href : '',
36243     /**
36244      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36245      */   
36246     size : 'xs',
36247     
36248     /**
36249      * @cfg {String} placetitle (center|bottom)
36250      */   
36251     placetitle : '',
36252     
36253     /**
36254      * @cfg {Boolean} isFitContainer defalut true
36255      */   
36256     isFitContainer : true, 
36257     
36258     /**
36259      * @cfg {Boolean} preventDefault defalut false
36260      */   
36261     preventDefault : false, 
36262     
36263     /**
36264      * @cfg {Boolean} inverse defalut false
36265      */   
36266     maskInverse : false, 
36267     
36268     getAutoCreate : function()
36269     {
36270         if(!this.isFitContainer){
36271             return this.getSplitAutoCreate();
36272         }
36273         
36274         var cls = 'masonry-brick masonry-brick-full';
36275         
36276         if(this.href.length){
36277             cls += ' masonry-brick-link';
36278         }
36279         
36280         if(this.bgimage.length){
36281             cls += ' masonry-brick-image';
36282         }
36283         
36284         if(this.maskInverse){
36285             cls += ' mask-inverse';
36286         }
36287         
36288         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36289             cls += ' enable-mask';
36290         }
36291         
36292         if(this.size){
36293             cls += ' masonry-' + this.size + '-brick';
36294         }
36295         
36296         if(this.placetitle.length){
36297             
36298             switch (this.placetitle) {
36299                 case 'center' :
36300                     cls += ' masonry-center-title';
36301                     break;
36302                 case 'bottom' :
36303                     cls += ' masonry-bottom-title';
36304                     break;
36305                 default:
36306                     break;
36307             }
36308             
36309         } else {
36310             if(!this.html.length && !this.bgimage.length){
36311                 cls += ' masonry-center-title';
36312             }
36313
36314             if(!this.html.length && this.bgimage.length){
36315                 cls += ' masonry-bottom-title';
36316             }
36317         }
36318         
36319         if(this.cls){
36320             cls += ' ' + this.cls;
36321         }
36322         
36323         var cfg = {
36324             tag: (this.href.length) ? 'a' : 'div',
36325             cls: cls,
36326             cn: [
36327                 {
36328                     tag: 'div',
36329                     cls: 'masonry-brick-mask'
36330                 },
36331                 {
36332                     tag: 'div',
36333                     cls: 'masonry-brick-paragraph',
36334                     cn: []
36335                 }
36336             ]
36337         };
36338         
36339         if(this.href.length){
36340             cfg.href = this.href;
36341         }
36342         
36343         var cn = cfg.cn[1].cn;
36344         
36345         if(this.title.length){
36346             cn.push({
36347                 tag: 'h4',
36348                 cls: 'masonry-brick-title',
36349                 html: this.title
36350             });
36351         }
36352         
36353         if(this.html.length){
36354             cn.push({
36355                 tag: 'p',
36356                 cls: 'masonry-brick-text',
36357                 html: this.html
36358             });
36359         }
36360         
36361         if (!this.title.length && !this.html.length) {
36362             cfg.cn[1].cls += ' hide';
36363         }
36364         
36365         if(this.bgimage.length){
36366             cfg.cn.push({
36367                 tag: 'img',
36368                 cls: 'masonry-brick-image-view',
36369                 src: this.bgimage
36370             });
36371         }
36372         
36373         if(this.videourl.length){
36374             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36375             // youtube support only?
36376             cfg.cn.push({
36377                 tag: 'iframe',
36378                 cls: 'masonry-brick-image-view',
36379                 src: vurl,
36380                 frameborder : 0,
36381                 allowfullscreen : true
36382             });
36383         }
36384         
36385         return cfg;
36386         
36387     },
36388     
36389     getSplitAutoCreate : function()
36390     {
36391         var cls = 'masonry-brick masonry-brick-split';
36392         
36393         if(this.href.length){
36394             cls += ' masonry-brick-link';
36395         }
36396         
36397         if(this.bgimage.length){
36398             cls += ' masonry-brick-image';
36399         }
36400         
36401         if(this.size){
36402             cls += ' masonry-' + this.size + '-brick';
36403         }
36404         
36405         switch (this.placetitle) {
36406             case 'center' :
36407                 cls += ' masonry-center-title';
36408                 break;
36409             case 'bottom' :
36410                 cls += ' masonry-bottom-title';
36411                 break;
36412             default:
36413                 if(!this.bgimage.length){
36414                     cls += ' masonry-center-title';
36415                 }
36416
36417                 if(this.bgimage.length){
36418                     cls += ' masonry-bottom-title';
36419                 }
36420                 break;
36421         }
36422         
36423         if(this.cls){
36424             cls += ' ' + this.cls;
36425         }
36426         
36427         var cfg = {
36428             tag: (this.href.length) ? 'a' : 'div',
36429             cls: cls,
36430             cn: [
36431                 {
36432                     tag: 'div',
36433                     cls: 'masonry-brick-split-head',
36434                     cn: [
36435                         {
36436                             tag: 'div',
36437                             cls: 'masonry-brick-paragraph',
36438                             cn: []
36439                         }
36440                     ]
36441                 },
36442                 {
36443                     tag: 'div',
36444                     cls: 'masonry-brick-split-body',
36445                     cn: []
36446                 }
36447             ]
36448         };
36449         
36450         if(this.href.length){
36451             cfg.href = this.href;
36452         }
36453         
36454         if(this.title.length){
36455             cfg.cn[0].cn[0].cn.push({
36456                 tag: 'h4',
36457                 cls: 'masonry-brick-title',
36458                 html: this.title
36459             });
36460         }
36461         
36462         if(this.html.length){
36463             cfg.cn[1].cn.push({
36464                 tag: 'p',
36465                 cls: 'masonry-brick-text',
36466                 html: this.html
36467             });
36468         }
36469
36470         if(this.bgimage.length){
36471             cfg.cn[0].cn.push({
36472                 tag: 'img',
36473                 cls: 'masonry-brick-image-view',
36474                 src: this.bgimage
36475             });
36476         }
36477         
36478         if(this.videourl.length){
36479             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36480             // youtube support only?
36481             cfg.cn[0].cn.cn.push({
36482                 tag: 'iframe',
36483                 cls: 'masonry-brick-image-view',
36484                 src: vurl,
36485                 frameborder : 0,
36486                 allowfullscreen : true
36487             });
36488         }
36489         
36490         return cfg;
36491     },
36492     
36493     initEvents: function() 
36494     {
36495         switch (this.size) {
36496             case 'xs' :
36497                 this.x = 1;
36498                 this.y = 1;
36499                 break;
36500             case 'sm' :
36501                 this.x = 2;
36502                 this.y = 2;
36503                 break;
36504             case 'md' :
36505             case 'md-left' :
36506             case 'md-right' :
36507                 this.x = 3;
36508                 this.y = 3;
36509                 break;
36510             case 'tall' :
36511                 this.x = 2;
36512                 this.y = 3;
36513                 break;
36514             case 'wide' :
36515                 this.x = 3;
36516                 this.y = 2;
36517                 break;
36518             case 'wide-thin' :
36519                 this.x = 3;
36520                 this.y = 1;
36521                 break;
36522                         
36523             default :
36524                 break;
36525         }
36526         
36527         if(Roo.isTouch){
36528             this.el.on('touchstart', this.onTouchStart, this);
36529             this.el.on('touchmove', this.onTouchMove, this);
36530             this.el.on('touchend', this.onTouchEnd, this);
36531             this.el.on('contextmenu', this.onContextMenu, this);
36532         } else {
36533             this.el.on('mouseenter'  ,this.enter, this);
36534             this.el.on('mouseleave', this.leave, this);
36535             this.el.on('click', this.onClick, this);
36536         }
36537         
36538         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36539             this.parent().bricks.push(this);   
36540         }
36541         
36542     },
36543     
36544     onClick: function(e, el)
36545     {
36546         var time = this.endTimer - this.startTimer;
36547         // Roo.log(e.preventDefault());
36548         if(Roo.isTouch){
36549             if(time > 1000){
36550                 e.preventDefault();
36551                 return;
36552             }
36553         }
36554         
36555         if(!this.preventDefault){
36556             return;
36557         }
36558         
36559         e.preventDefault();
36560         
36561         if (this.activeClass != '') {
36562             this.selectBrick();
36563         }
36564         
36565         this.fireEvent('click', this, e);
36566     },
36567     
36568     enter: function(e, el)
36569     {
36570         e.preventDefault();
36571         
36572         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36573             return;
36574         }
36575         
36576         if(this.bgimage.length && this.html.length){
36577             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36578         }
36579     },
36580     
36581     leave: function(e, el)
36582     {
36583         e.preventDefault();
36584         
36585         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36586             return;
36587         }
36588         
36589         if(this.bgimage.length && this.html.length){
36590             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36591         }
36592     },
36593     
36594     onTouchStart: function(e, el)
36595     {
36596 //        e.preventDefault();
36597         
36598         this.touchmoved = false;
36599         
36600         if(!this.isFitContainer){
36601             return;
36602         }
36603         
36604         if(!this.bgimage.length || !this.html.length){
36605             return;
36606         }
36607         
36608         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36609         
36610         this.timer = new Date().getTime();
36611         
36612     },
36613     
36614     onTouchMove: function(e, el)
36615     {
36616         this.touchmoved = true;
36617     },
36618     
36619     onContextMenu : function(e,el)
36620     {
36621         e.preventDefault();
36622         e.stopPropagation();
36623         return false;
36624     },
36625     
36626     onTouchEnd: function(e, el)
36627     {
36628 //        e.preventDefault();
36629         
36630         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36631         
36632             this.leave(e,el);
36633             
36634             return;
36635         }
36636         
36637         if(!this.bgimage.length || !this.html.length){
36638             
36639             if(this.href.length){
36640                 window.location.href = this.href;
36641             }
36642             
36643             return;
36644         }
36645         
36646         if(!this.isFitContainer){
36647             return;
36648         }
36649         
36650         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36651         
36652         window.location.href = this.href;
36653     },
36654     
36655     //selection on single brick only
36656     selectBrick : function() {
36657         
36658         if (!this.parentId) {
36659             return;
36660         }
36661         
36662         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36663         var index = m.selectedBrick.indexOf(this.id);
36664         
36665         if ( index > -1) {
36666             m.selectedBrick.splice(index,1);
36667             this.el.removeClass(this.activeClass);
36668             return;
36669         }
36670         
36671         for(var i = 0; i < m.selectedBrick.length; i++) {
36672             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36673             b.el.removeClass(b.activeClass);
36674         }
36675         
36676         m.selectedBrick = [];
36677         
36678         m.selectedBrick.push(this.id);
36679         this.el.addClass(this.activeClass);
36680         return;
36681     },
36682     
36683     isSelected : function(){
36684         return this.el.hasClass(this.activeClass);
36685         
36686     }
36687 });
36688
36689 Roo.apply(Roo.bootstrap.MasonryBrick, {
36690     
36691     //groups: {},
36692     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36693      /**
36694     * register a Masonry Brick
36695     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36696     */
36697     
36698     register : function(brick)
36699     {
36700         //this.groups[brick.id] = brick;
36701         this.groups.add(brick.id, brick);
36702     },
36703     /**
36704     * fetch a  masonry brick based on the masonry brick ID
36705     * @param {string} the masonry brick to add
36706     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36707     */
36708     
36709     get: function(brick_id) 
36710     {
36711         // if (typeof(this.groups[brick_id]) == 'undefined') {
36712         //     return false;
36713         // }
36714         // return this.groups[brick_id] ;
36715         
36716         if(this.groups.key(brick_id)) {
36717             return this.groups.key(brick_id);
36718         }
36719         
36720         return false;
36721     }
36722     
36723     
36724     
36725 });
36726
36727  /*
36728  * - LGPL
36729  *
36730  * element
36731  * 
36732  */
36733
36734 /**
36735  * @class Roo.bootstrap.Brick
36736  * @extends Roo.bootstrap.Component
36737  * Bootstrap Brick class
36738  * 
36739  * @constructor
36740  * Create a new Brick
36741  * @param {Object} config The config object
36742  */
36743
36744 Roo.bootstrap.Brick = function(config){
36745     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36746     
36747     this.addEvents({
36748         // raw events
36749         /**
36750          * @event click
36751          * When a Brick is click
36752          * @param {Roo.bootstrap.Brick} this
36753          * @param {Roo.EventObject} e
36754          */
36755         "click" : true
36756     });
36757 };
36758
36759 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36760     
36761     /**
36762      * @cfg {String} title
36763      */   
36764     title : '',
36765     /**
36766      * @cfg {String} html
36767      */   
36768     html : '',
36769     /**
36770      * @cfg {String} bgimage
36771      */   
36772     bgimage : '',
36773     /**
36774      * @cfg {String} cls
36775      */   
36776     cls : '',
36777     /**
36778      * @cfg {String} href
36779      */   
36780     href : '',
36781     /**
36782      * @cfg {String} video
36783      */   
36784     video : '',
36785     /**
36786      * @cfg {Boolean} square
36787      */   
36788     square : true,
36789     
36790     getAutoCreate : function()
36791     {
36792         var cls = 'roo-brick';
36793         
36794         if(this.href.length){
36795             cls += ' roo-brick-link';
36796         }
36797         
36798         if(this.bgimage.length){
36799             cls += ' roo-brick-image';
36800         }
36801         
36802         if(!this.html.length && !this.bgimage.length){
36803             cls += ' roo-brick-center-title';
36804         }
36805         
36806         if(!this.html.length && this.bgimage.length){
36807             cls += ' roo-brick-bottom-title';
36808         }
36809         
36810         if(this.cls){
36811             cls += ' ' + this.cls;
36812         }
36813         
36814         var cfg = {
36815             tag: (this.href.length) ? 'a' : 'div',
36816             cls: cls,
36817             cn: [
36818                 {
36819                     tag: 'div',
36820                     cls: 'roo-brick-paragraph',
36821                     cn: []
36822                 }
36823             ]
36824         };
36825         
36826         if(this.href.length){
36827             cfg.href = this.href;
36828         }
36829         
36830         var cn = cfg.cn[0].cn;
36831         
36832         if(this.title.length){
36833             cn.push({
36834                 tag: 'h4',
36835                 cls: 'roo-brick-title',
36836                 html: this.title
36837             });
36838         }
36839         
36840         if(this.html.length){
36841             cn.push({
36842                 tag: 'p',
36843                 cls: 'roo-brick-text',
36844                 html: this.html
36845             });
36846         } else {
36847             cn.cls += ' hide';
36848         }
36849         
36850         if(this.bgimage.length){
36851             cfg.cn.push({
36852                 tag: 'img',
36853                 cls: 'roo-brick-image-view',
36854                 src: this.bgimage
36855             });
36856         }
36857         
36858         return cfg;
36859     },
36860     
36861     initEvents: function() 
36862     {
36863         if(this.title.length || this.html.length){
36864             this.el.on('mouseenter'  ,this.enter, this);
36865             this.el.on('mouseleave', this.leave, this);
36866         }
36867         
36868         Roo.EventManager.onWindowResize(this.resize, this); 
36869         
36870         if(this.bgimage.length){
36871             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36872             this.imageEl.on('load', this.onImageLoad, this);
36873             return;
36874         }
36875         
36876         this.resize();
36877     },
36878     
36879     onImageLoad : function()
36880     {
36881         this.resize();
36882     },
36883     
36884     resize : function()
36885     {
36886         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36887         
36888         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36889         
36890         if(this.bgimage.length){
36891             var image = this.el.select('.roo-brick-image-view', true).first();
36892             
36893             image.setWidth(paragraph.getWidth());
36894             
36895             if(this.square){
36896                 image.setHeight(paragraph.getWidth());
36897             }
36898             
36899             this.el.setHeight(image.getHeight());
36900             paragraph.setHeight(image.getHeight());
36901             
36902         }
36903         
36904     },
36905     
36906     enter: function(e, el)
36907     {
36908         e.preventDefault();
36909         
36910         if(this.bgimage.length){
36911             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36912             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36913         }
36914     },
36915     
36916     leave: function(e, el)
36917     {
36918         e.preventDefault();
36919         
36920         if(this.bgimage.length){
36921             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36922             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36923         }
36924     }
36925     
36926 });
36927
36928  
36929
36930  /*
36931  * - LGPL
36932  *
36933  * Number field 
36934  */
36935
36936 /**
36937  * @class Roo.bootstrap.NumberField
36938  * @extends Roo.bootstrap.Input
36939  * Bootstrap NumberField class
36940  * 
36941  * 
36942  * 
36943  * 
36944  * @constructor
36945  * Create a new NumberField
36946  * @param {Object} config The config object
36947  */
36948
36949 Roo.bootstrap.NumberField = function(config){
36950     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36951 };
36952
36953 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36954     
36955     /**
36956      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36957      */
36958     allowDecimals : true,
36959     /**
36960      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36961      */
36962     decimalSeparator : ".",
36963     /**
36964      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36965      */
36966     decimalPrecision : 2,
36967     /**
36968      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36969      */
36970     allowNegative : true,
36971     
36972     /**
36973      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36974      */
36975     allowZero: true,
36976     /**
36977      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36978      */
36979     minValue : Number.NEGATIVE_INFINITY,
36980     /**
36981      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36982      */
36983     maxValue : Number.MAX_VALUE,
36984     /**
36985      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36986      */
36987     minText : "The minimum value for this field is {0}",
36988     /**
36989      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36990      */
36991     maxText : "The maximum value for this field is {0}",
36992     /**
36993      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36994      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36995      */
36996     nanText : "{0} is not a valid number",
36997     /**
36998      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36999      */
37000     thousandsDelimiter : false,
37001     /**
37002      * @cfg {String} valueAlign alignment of value
37003      */
37004     valueAlign : "left",
37005
37006     getAutoCreate : function()
37007     {
37008         var hiddenInput = {
37009             tag: 'input',
37010             type: 'hidden',
37011             id: Roo.id(),
37012             cls: 'hidden-number-input'
37013         };
37014         
37015         if (this.name) {
37016             hiddenInput.name = this.name;
37017         }
37018         
37019         this.name = '';
37020         
37021         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37022         
37023         this.name = hiddenInput.name;
37024         
37025         if(cfg.cn.length > 0) {
37026             cfg.cn.push(hiddenInput);
37027         }
37028         
37029         return cfg;
37030     },
37031
37032     // private
37033     initEvents : function()
37034     {   
37035         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37036         
37037         var allowed = "0123456789";
37038         
37039         if(this.allowDecimals){
37040             allowed += this.decimalSeparator;
37041         }
37042         
37043         if(this.allowNegative){
37044             allowed += "-";
37045         }
37046         
37047         if(this.thousandsDelimiter) {
37048             allowed += ",";
37049         }
37050         
37051         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37052         
37053         var keyPress = function(e){
37054             
37055             var k = e.getKey();
37056             
37057             var c = e.getCharCode();
37058             
37059             if(
37060                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37061                     allowed.indexOf(String.fromCharCode(c)) === -1
37062             ){
37063                 e.stopEvent();
37064                 return;
37065             }
37066             
37067             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37068                 return;
37069             }
37070             
37071             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37072                 e.stopEvent();
37073             }
37074         };
37075         
37076         this.el.on("keypress", keyPress, this);
37077     },
37078     
37079     validateValue : function(value)
37080     {
37081         
37082         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37083             return false;
37084         }
37085         
37086         var num = this.parseValue(value);
37087         
37088         if(isNaN(num)){
37089             this.markInvalid(String.format(this.nanText, value));
37090             return false;
37091         }
37092         
37093         if(num < this.minValue){
37094             this.markInvalid(String.format(this.minText, this.minValue));
37095             return false;
37096         }
37097         
37098         if(num > this.maxValue){
37099             this.markInvalid(String.format(this.maxText, this.maxValue));
37100             return false;
37101         }
37102         
37103         return true;
37104     },
37105
37106     getValue : function()
37107     {
37108         var v = this.hiddenEl().getValue();
37109         
37110         return this.fixPrecision(this.parseValue(v));
37111     },
37112
37113     parseValue : function(value)
37114     {
37115         if(this.thousandsDelimiter) {
37116             value += "";
37117             r = new RegExp(",", "g");
37118             value = value.replace(r, "");
37119         }
37120         
37121         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37122         return isNaN(value) ? '' : value;
37123     },
37124
37125     fixPrecision : function(value)
37126     {
37127         if(this.thousandsDelimiter) {
37128             value += "";
37129             r = new RegExp(",", "g");
37130             value = value.replace(r, "");
37131         }
37132         
37133         var nan = isNaN(value);
37134         
37135         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37136             return nan ? '' : value;
37137         }
37138         return parseFloat(value).toFixed(this.decimalPrecision);
37139     },
37140
37141     setValue : function(v)
37142     {
37143         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37144         
37145         this.value = v;
37146         
37147         if(this.rendered){
37148             
37149             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37150             
37151             this.inputEl().dom.value = (v == '') ? '' :
37152                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37153             
37154             if(!this.allowZero && v === '0') {
37155                 this.hiddenEl().dom.value = '';
37156                 this.inputEl().dom.value = '';
37157             }
37158             
37159             this.validate();
37160         }
37161     },
37162
37163     decimalPrecisionFcn : function(v)
37164     {
37165         return Math.floor(v);
37166     },
37167
37168     beforeBlur : function()
37169     {
37170         var v = this.parseValue(this.getRawValue());
37171         
37172         if(v || v === 0 || v === ''){
37173             this.setValue(v);
37174         }
37175     },
37176     
37177     hiddenEl : function()
37178     {
37179         return this.el.select('input.hidden-number-input',true).first();
37180     }
37181     
37182 });
37183
37184  
37185
37186 /*
37187 * Licence: LGPL
37188 */
37189
37190 /**
37191  * @class Roo.bootstrap.DocumentSlider
37192  * @extends Roo.bootstrap.Component
37193  * Bootstrap DocumentSlider class
37194  * 
37195  * @constructor
37196  * Create a new DocumentViewer
37197  * @param {Object} config The config object
37198  */
37199
37200 Roo.bootstrap.DocumentSlider = function(config){
37201     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37202     
37203     this.files = [];
37204     
37205     this.addEvents({
37206         /**
37207          * @event initial
37208          * Fire after initEvent
37209          * @param {Roo.bootstrap.DocumentSlider} this
37210          */
37211         "initial" : true,
37212         /**
37213          * @event update
37214          * Fire after update
37215          * @param {Roo.bootstrap.DocumentSlider} this
37216          */
37217         "update" : true,
37218         /**
37219          * @event click
37220          * Fire after click
37221          * @param {Roo.bootstrap.DocumentSlider} this
37222          */
37223         "click" : true
37224     });
37225 };
37226
37227 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37228     
37229     files : false,
37230     
37231     indicator : 0,
37232     
37233     getAutoCreate : function()
37234     {
37235         var cfg = {
37236             tag : 'div',
37237             cls : 'roo-document-slider',
37238             cn : [
37239                 {
37240                     tag : 'div',
37241                     cls : 'roo-document-slider-header',
37242                     cn : [
37243                         {
37244                             tag : 'div',
37245                             cls : 'roo-document-slider-header-title'
37246                         }
37247                     ]
37248                 },
37249                 {
37250                     tag : 'div',
37251                     cls : 'roo-document-slider-body',
37252                     cn : [
37253                         {
37254                             tag : 'div',
37255                             cls : 'roo-document-slider-prev',
37256                             cn : [
37257                                 {
37258                                     tag : 'i',
37259                                     cls : 'fa fa-chevron-left'
37260                                 }
37261                             ]
37262                         },
37263                         {
37264                             tag : 'div',
37265                             cls : 'roo-document-slider-thumb',
37266                             cn : [
37267                                 {
37268                                     tag : 'img',
37269                                     cls : 'roo-document-slider-image'
37270                                 }
37271                             ]
37272                         },
37273                         {
37274                             tag : 'div',
37275                             cls : 'roo-document-slider-next',
37276                             cn : [
37277                                 {
37278                                     tag : 'i',
37279                                     cls : 'fa fa-chevron-right'
37280                                 }
37281                             ]
37282                         }
37283                     ]
37284                 }
37285             ]
37286         };
37287         
37288         return cfg;
37289     },
37290     
37291     initEvents : function()
37292     {
37293         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37294         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37295         
37296         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37297         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37298         
37299         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37300         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37301         
37302         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37303         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37304         
37305         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37306         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37307         
37308         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37309         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37310         
37311         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37312         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37313         
37314         this.thumbEl.on('click', this.onClick, this);
37315         
37316         this.prevIndicator.on('click', this.prev, this);
37317         
37318         this.nextIndicator.on('click', this.next, this);
37319         
37320     },
37321     
37322     initial : function()
37323     {
37324         if(this.files.length){
37325             this.indicator = 1;
37326             this.update()
37327         }
37328         
37329         this.fireEvent('initial', this);
37330     },
37331     
37332     update : function()
37333     {
37334         this.imageEl.attr('src', this.files[this.indicator - 1]);
37335         
37336         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37337         
37338         this.prevIndicator.show();
37339         
37340         if(this.indicator == 1){
37341             this.prevIndicator.hide();
37342         }
37343         
37344         this.nextIndicator.show();
37345         
37346         if(this.indicator == this.files.length){
37347             this.nextIndicator.hide();
37348         }
37349         
37350         this.thumbEl.scrollTo('top');
37351         
37352         this.fireEvent('update', this);
37353     },
37354     
37355     onClick : function(e)
37356     {
37357         e.preventDefault();
37358         
37359         this.fireEvent('click', this);
37360     },
37361     
37362     prev : function(e)
37363     {
37364         e.preventDefault();
37365         
37366         this.indicator = Math.max(1, this.indicator - 1);
37367         
37368         this.update();
37369     },
37370     
37371     next : function(e)
37372     {
37373         e.preventDefault();
37374         
37375         this.indicator = Math.min(this.files.length, this.indicator + 1);
37376         
37377         this.update();
37378     }
37379 });
37380 /*
37381  * - LGPL
37382  *
37383  * RadioSet
37384  *
37385  *
37386  */
37387
37388 /**
37389  * @class Roo.bootstrap.RadioSet
37390  * @extends Roo.bootstrap.Input
37391  * Bootstrap RadioSet class
37392  * @cfg {String} indicatorpos (left|right) default left
37393  * @cfg {Boolean} inline (true|false) inline the element (default true)
37394  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37395  * @constructor
37396  * Create a new RadioSet
37397  * @param {Object} config The config object
37398  */
37399
37400 Roo.bootstrap.RadioSet = function(config){
37401     
37402     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37403     
37404     this.radioes = [];
37405     
37406     Roo.bootstrap.RadioSet.register(this);
37407     
37408     this.addEvents({
37409         /**
37410         * @event check
37411         * Fires when the element is checked or unchecked.
37412         * @param {Roo.bootstrap.RadioSet} this This radio
37413         * @param {Roo.bootstrap.Radio} item The checked item
37414         */
37415        check : true,
37416        /**
37417         * @event click
37418         * Fires when the element is click.
37419         * @param {Roo.bootstrap.RadioSet} this This radio set
37420         * @param {Roo.bootstrap.Radio} item The checked item
37421         * @param {Roo.EventObject} e The event object
37422         */
37423        click : true
37424     });
37425     
37426 };
37427
37428 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37429
37430     radioes : false,
37431     
37432     inline : true,
37433     
37434     weight : '',
37435     
37436     indicatorpos : 'left',
37437     
37438     getAutoCreate : function()
37439     {
37440         var label = {
37441             tag : 'label',
37442             cls : 'roo-radio-set-label',
37443             cn : [
37444                 {
37445                     tag : 'span',
37446                     html : this.fieldLabel
37447                 }
37448             ]
37449         };
37450         if (Roo.bootstrap.version == 3) {
37451             
37452             
37453             if(this.indicatorpos == 'left'){
37454                 label.cn.unshift({
37455                     tag : 'i',
37456                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37457                     tooltip : 'This field is required'
37458                 });
37459             } else {
37460                 label.cn.push({
37461                     tag : 'i',
37462                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37463                     tooltip : 'This field is required'
37464                 });
37465             }
37466         }
37467         var items = {
37468             tag : 'div',
37469             cls : 'roo-radio-set-items'
37470         };
37471         
37472         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37473         
37474         if (align === 'left' && this.fieldLabel.length) {
37475             
37476             items = {
37477                 cls : "roo-radio-set-right", 
37478                 cn: [
37479                     items
37480                 ]
37481             };
37482             
37483             if(this.labelWidth > 12){
37484                 label.style = "width: " + this.labelWidth + 'px';
37485             }
37486             
37487             if(this.labelWidth < 13 && this.labelmd == 0){
37488                 this.labelmd = this.labelWidth;
37489             }
37490             
37491             if(this.labellg > 0){
37492                 label.cls += ' col-lg-' + this.labellg;
37493                 items.cls += ' col-lg-' + (12 - this.labellg);
37494             }
37495             
37496             if(this.labelmd > 0){
37497                 label.cls += ' col-md-' + this.labelmd;
37498                 items.cls += ' col-md-' + (12 - this.labelmd);
37499             }
37500             
37501             if(this.labelsm > 0){
37502                 label.cls += ' col-sm-' + this.labelsm;
37503                 items.cls += ' col-sm-' + (12 - this.labelsm);
37504             }
37505             
37506             if(this.labelxs > 0){
37507                 label.cls += ' col-xs-' + this.labelxs;
37508                 items.cls += ' col-xs-' + (12 - this.labelxs);
37509             }
37510         }
37511         
37512         var cfg = {
37513             tag : 'div',
37514             cls : 'roo-radio-set',
37515             cn : [
37516                 {
37517                     tag : 'input',
37518                     cls : 'roo-radio-set-input',
37519                     type : 'hidden',
37520                     name : this.name,
37521                     value : this.value ? this.value :  ''
37522                 },
37523                 label,
37524                 items
37525             ]
37526         };
37527         
37528         if(this.weight.length){
37529             cfg.cls += ' roo-radio-' + this.weight;
37530         }
37531         
37532         if(this.inline) {
37533             cfg.cls += ' roo-radio-set-inline';
37534         }
37535         
37536         var settings=this;
37537         ['xs','sm','md','lg'].map(function(size){
37538             if (settings[size]) {
37539                 cfg.cls += ' col-' + size + '-' + settings[size];
37540             }
37541         });
37542         
37543         return cfg;
37544         
37545     },
37546
37547     initEvents : function()
37548     {
37549         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37550         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37551         
37552         if(!this.fieldLabel.length){
37553             this.labelEl.hide();
37554         }
37555         
37556         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37557         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37558         
37559         this.indicator = this.indicatorEl();
37560         
37561         if(this.indicator){
37562             this.indicator.addClass('invisible');
37563         }
37564         
37565         this.originalValue = this.getValue();
37566         
37567     },
37568     
37569     inputEl: function ()
37570     {
37571         return this.el.select('.roo-radio-set-input', true).first();
37572     },
37573     
37574     getChildContainer : function()
37575     {
37576         return this.itemsEl;
37577     },
37578     
37579     register : function(item)
37580     {
37581         this.radioes.push(item);
37582         
37583     },
37584     
37585     validate : function()
37586     {   
37587         if(this.getVisibilityEl().hasClass('hidden')){
37588             return true;
37589         }
37590         
37591         var valid = false;
37592         
37593         Roo.each(this.radioes, function(i){
37594             if(!i.checked){
37595                 return;
37596             }
37597             
37598             valid = true;
37599             return false;
37600         });
37601         
37602         if(this.allowBlank) {
37603             return true;
37604         }
37605         
37606         if(this.disabled || valid){
37607             this.markValid();
37608             return true;
37609         }
37610         
37611         this.markInvalid();
37612         return false;
37613         
37614     },
37615     
37616     markValid : function()
37617     {
37618         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37619             this.indicatorEl().removeClass('visible');
37620             this.indicatorEl().addClass('invisible');
37621         }
37622         
37623         
37624         if (Roo.bootstrap.version == 3) {
37625             this.el.removeClass([this.invalidClass, this.validClass]);
37626             this.el.addClass(this.validClass);
37627         } else {
37628             this.el.removeClass(['is-invalid','is-valid']);
37629             this.el.addClass(['is-valid']);
37630         }
37631         this.fireEvent('valid', this);
37632     },
37633     
37634     markInvalid : function(msg)
37635     {
37636         if(this.allowBlank || this.disabled){
37637             return;
37638         }
37639         
37640         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37641             this.indicatorEl().removeClass('invisible');
37642             this.indicatorEl().addClass('visible');
37643         }
37644         if (Roo.bootstrap.version == 3) {
37645             this.el.removeClass([this.invalidClass, this.validClass]);
37646             this.el.addClass(this.invalidClass);
37647         } else {
37648             this.el.removeClass(['is-invalid','is-valid']);
37649             this.el.addClass(['is-invalid']);
37650         }
37651         
37652         this.fireEvent('invalid', this, msg);
37653         
37654     },
37655     
37656     setValue : function(v, suppressEvent)
37657     {   
37658         if(this.value === v){
37659             return;
37660         }
37661         
37662         this.value = v;
37663         
37664         if(this.rendered){
37665             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37666         }
37667         
37668         Roo.each(this.radioes, function(i){
37669             i.checked = false;
37670             i.el.removeClass('checked');
37671         });
37672         
37673         Roo.each(this.radioes, function(i){
37674             
37675             if(i.value === v || i.value.toString() === v.toString()){
37676                 i.checked = true;
37677                 i.el.addClass('checked');
37678                 
37679                 if(suppressEvent !== true){
37680                     this.fireEvent('check', this, i);
37681                 }
37682                 
37683                 return false;
37684             }
37685             
37686         }, this);
37687         
37688         this.validate();
37689     },
37690     
37691     clearInvalid : function(){
37692         
37693         if(!this.el || this.preventMark){
37694             return;
37695         }
37696         
37697         this.el.removeClass([this.invalidClass]);
37698         
37699         this.fireEvent('valid', this);
37700     }
37701     
37702 });
37703
37704 Roo.apply(Roo.bootstrap.RadioSet, {
37705     
37706     groups: {},
37707     
37708     register : function(set)
37709     {
37710         this.groups[set.name] = set;
37711     },
37712     
37713     get: function(name) 
37714     {
37715         if (typeof(this.groups[name]) == 'undefined') {
37716             return false;
37717         }
37718         
37719         return this.groups[name] ;
37720     }
37721     
37722 });
37723 /*
37724  * Based on:
37725  * Ext JS Library 1.1.1
37726  * Copyright(c) 2006-2007, Ext JS, LLC.
37727  *
37728  * Originally Released Under LGPL - original licence link has changed is not relivant.
37729  *
37730  * Fork - LGPL
37731  * <script type="text/javascript">
37732  */
37733
37734
37735 /**
37736  * @class Roo.bootstrap.SplitBar
37737  * @extends Roo.util.Observable
37738  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37739  * <br><br>
37740  * Usage:
37741  * <pre><code>
37742 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37743                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37744 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37745 split.minSize = 100;
37746 split.maxSize = 600;
37747 split.animate = true;
37748 split.on('moved', splitterMoved);
37749 </code></pre>
37750  * @constructor
37751  * Create a new SplitBar
37752  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37753  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37754  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37755  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37756                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37757                         position of the SplitBar).
37758  */
37759 Roo.bootstrap.SplitBar = function(cfg){
37760     
37761     /** @private */
37762     
37763     //{
37764     //  dragElement : elm
37765     //  resizingElement: el,
37766         // optional..
37767     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37768     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37769         // existingProxy ???
37770     //}
37771     
37772     this.el = Roo.get(cfg.dragElement, true);
37773     this.el.dom.unselectable = "on";
37774     /** @private */
37775     this.resizingEl = Roo.get(cfg.resizingElement, true);
37776
37777     /**
37778      * @private
37779      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37780      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37781      * @type Number
37782      */
37783     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37784     
37785     /**
37786      * The minimum size of the resizing element. (Defaults to 0)
37787      * @type Number
37788      */
37789     this.minSize = 0;
37790     
37791     /**
37792      * The maximum size of the resizing element. (Defaults to 2000)
37793      * @type Number
37794      */
37795     this.maxSize = 2000;
37796     
37797     /**
37798      * Whether to animate the transition to the new size
37799      * @type Boolean
37800      */
37801     this.animate = false;
37802     
37803     /**
37804      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37805      * @type Boolean
37806      */
37807     this.useShim = false;
37808     
37809     /** @private */
37810     this.shim = null;
37811     
37812     if(!cfg.existingProxy){
37813         /** @private */
37814         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37815     }else{
37816         this.proxy = Roo.get(cfg.existingProxy).dom;
37817     }
37818     /** @private */
37819     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37820     
37821     /** @private */
37822     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37823     
37824     /** @private */
37825     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37826     
37827     /** @private */
37828     this.dragSpecs = {};
37829     
37830     /**
37831      * @private The adapter to use to positon and resize elements
37832      */
37833     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37834     this.adapter.init(this);
37835     
37836     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37837         /** @private */
37838         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37839         this.el.addClass("roo-splitbar-h");
37840     }else{
37841         /** @private */
37842         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37843         this.el.addClass("roo-splitbar-v");
37844     }
37845     
37846     this.addEvents({
37847         /**
37848          * @event resize
37849          * Fires when the splitter is moved (alias for {@link #event-moved})
37850          * @param {Roo.bootstrap.SplitBar} this
37851          * @param {Number} newSize the new width or height
37852          */
37853         "resize" : true,
37854         /**
37855          * @event moved
37856          * Fires when the splitter is moved
37857          * @param {Roo.bootstrap.SplitBar} this
37858          * @param {Number} newSize the new width or height
37859          */
37860         "moved" : true,
37861         /**
37862          * @event beforeresize
37863          * Fires before the splitter is dragged
37864          * @param {Roo.bootstrap.SplitBar} this
37865          */
37866         "beforeresize" : true,
37867
37868         "beforeapply" : true
37869     });
37870
37871     Roo.util.Observable.call(this);
37872 };
37873
37874 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37875     onStartProxyDrag : function(x, y){
37876         this.fireEvent("beforeresize", this);
37877         if(!this.overlay){
37878             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37879             o.unselectable();
37880             o.enableDisplayMode("block");
37881             // all splitbars share the same overlay
37882             Roo.bootstrap.SplitBar.prototype.overlay = o;
37883         }
37884         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37885         this.overlay.show();
37886         Roo.get(this.proxy).setDisplayed("block");
37887         var size = this.adapter.getElementSize(this);
37888         this.activeMinSize = this.getMinimumSize();;
37889         this.activeMaxSize = this.getMaximumSize();;
37890         var c1 = size - this.activeMinSize;
37891         var c2 = Math.max(this.activeMaxSize - size, 0);
37892         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37893             this.dd.resetConstraints();
37894             this.dd.setXConstraint(
37895                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37896                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37897             );
37898             this.dd.setYConstraint(0, 0);
37899         }else{
37900             this.dd.resetConstraints();
37901             this.dd.setXConstraint(0, 0);
37902             this.dd.setYConstraint(
37903                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37904                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37905             );
37906          }
37907         this.dragSpecs.startSize = size;
37908         this.dragSpecs.startPoint = [x, y];
37909         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37910     },
37911     
37912     /** 
37913      * @private Called after the drag operation by the DDProxy
37914      */
37915     onEndProxyDrag : function(e){
37916         Roo.get(this.proxy).setDisplayed(false);
37917         var endPoint = Roo.lib.Event.getXY(e);
37918         if(this.overlay){
37919             this.overlay.hide();
37920         }
37921         var newSize;
37922         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37923             newSize = this.dragSpecs.startSize + 
37924                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37925                     endPoint[0] - this.dragSpecs.startPoint[0] :
37926                     this.dragSpecs.startPoint[0] - endPoint[0]
37927                 );
37928         }else{
37929             newSize = this.dragSpecs.startSize + 
37930                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37931                     endPoint[1] - this.dragSpecs.startPoint[1] :
37932                     this.dragSpecs.startPoint[1] - endPoint[1]
37933                 );
37934         }
37935         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37936         if(newSize != this.dragSpecs.startSize){
37937             if(this.fireEvent('beforeapply', this, newSize) !== false){
37938                 this.adapter.setElementSize(this, newSize);
37939                 this.fireEvent("moved", this, newSize);
37940                 this.fireEvent("resize", this, newSize);
37941             }
37942         }
37943     },
37944     
37945     /**
37946      * Get the adapter this SplitBar uses
37947      * @return The adapter object
37948      */
37949     getAdapter : function(){
37950         return this.adapter;
37951     },
37952     
37953     /**
37954      * Set the adapter this SplitBar uses
37955      * @param {Object} adapter A SplitBar adapter object
37956      */
37957     setAdapter : function(adapter){
37958         this.adapter = adapter;
37959         this.adapter.init(this);
37960     },
37961     
37962     /**
37963      * Gets the minimum size for the resizing element
37964      * @return {Number} The minimum size
37965      */
37966     getMinimumSize : function(){
37967         return this.minSize;
37968     },
37969     
37970     /**
37971      * Sets the minimum size for the resizing element
37972      * @param {Number} minSize The minimum size
37973      */
37974     setMinimumSize : function(minSize){
37975         this.minSize = minSize;
37976     },
37977     
37978     /**
37979      * Gets the maximum size for the resizing element
37980      * @return {Number} The maximum size
37981      */
37982     getMaximumSize : function(){
37983         return this.maxSize;
37984     },
37985     
37986     /**
37987      * Sets the maximum size for the resizing element
37988      * @param {Number} maxSize The maximum size
37989      */
37990     setMaximumSize : function(maxSize){
37991         this.maxSize = maxSize;
37992     },
37993     
37994     /**
37995      * Sets the initialize size for the resizing element
37996      * @param {Number} size The initial size
37997      */
37998     setCurrentSize : function(size){
37999         var oldAnimate = this.animate;
38000         this.animate = false;
38001         this.adapter.setElementSize(this, size);
38002         this.animate = oldAnimate;
38003     },
38004     
38005     /**
38006      * Destroy this splitbar. 
38007      * @param {Boolean} removeEl True to remove the element
38008      */
38009     destroy : function(removeEl){
38010         if(this.shim){
38011             this.shim.remove();
38012         }
38013         this.dd.unreg();
38014         this.proxy.parentNode.removeChild(this.proxy);
38015         if(removeEl){
38016             this.el.remove();
38017         }
38018     }
38019 });
38020
38021 /**
38022  * @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.
38023  */
38024 Roo.bootstrap.SplitBar.createProxy = function(dir){
38025     var proxy = new Roo.Element(document.createElement("div"));
38026     proxy.unselectable();
38027     var cls = 'roo-splitbar-proxy';
38028     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38029     document.body.appendChild(proxy.dom);
38030     return proxy.dom;
38031 };
38032
38033 /** 
38034  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38035  * Default Adapter. It assumes the splitter and resizing element are not positioned
38036  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38037  */
38038 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38039 };
38040
38041 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38042     // do nothing for now
38043     init : function(s){
38044     
38045     },
38046     /**
38047      * Called before drag operations to get the current size of the resizing element. 
38048      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38049      */
38050      getElementSize : function(s){
38051         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38052             return s.resizingEl.getWidth();
38053         }else{
38054             return s.resizingEl.getHeight();
38055         }
38056     },
38057     
38058     /**
38059      * Called after drag operations to set the size of the resizing element.
38060      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38061      * @param {Number} newSize The new size to set
38062      * @param {Function} onComplete A function to be invoked when resizing is complete
38063      */
38064     setElementSize : function(s, newSize, onComplete){
38065         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38066             if(!s.animate){
38067                 s.resizingEl.setWidth(newSize);
38068                 if(onComplete){
38069                     onComplete(s, newSize);
38070                 }
38071             }else{
38072                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38073             }
38074         }else{
38075             
38076             if(!s.animate){
38077                 s.resizingEl.setHeight(newSize);
38078                 if(onComplete){
38079                     onComplete(s, newSize);
38080                 }
38081             }else{
38082                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38083             }
38084         }
38085     }
38086 };
38087
38088 /** 
38089  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38090  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38091  * Adapter that  moves the splitter element to align with the resized sizing element. 
38092  * Used with an absolute positioned SplitBar.
38093  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38094  * document.body, make sure you assign an id to the body element.
38095  */
38096 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38097     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38098     this.container = Roo.get(container);
38099 };
38100
38101 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38102     init : function(s){
38103         this.basic.init(s);
38104     },
38105     
38106     getElementSize : function(s){
38107         return this.basic.getElementSize(s);
38108     },
38109     
38110     setElementSize : function(s, newSize, onComplete){
38111         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38112     },
38113     
38114     moveSplitter : function(s){
38115         var yes = Roo.bootstrap.SplitBar;
38116         switch(s.placement){
38117             case yes.LEFT:
38118                 s.el.setX(s.resizingEl.getRight());
38119                 break;
38120             case yes.RIGHT:
38121                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38122                 break;
38123             case yes.TOP:
38124                 s.el.setY(s.resizingEl.getBottom());
38125                 break;
38126             case yes.BOTTOM:
38127                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38128                 break;
38129         }
38130     }
38131 };
38132
38133 /**
38134  * Orientation constant - Create a vertical SplitBar
38135  * @static
38136  * @type Number
38137  */
38138 Roo.bootstrap.SplitBar.VERTICAL = 1;
38139
38140 /**
38141  * Orientation constant - Create a horizontal SplitBar
38142  * @static
38143  * @type Number
38144  */
38145 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38146
38147 /**
38148  * Placement constant - The resizing element is to the left of the splitter element
38149  * @static
38150  * @type Number
38151  */
38152 Roo.bootstrap.SplitBar.LEFT = 1;
38153
38154 /**
38155  * Placement constant - The resizing element is to the right of the splitter element
38156  * @static
38157  * @type Number
38158  */
38159 Roo.bootstrap.SplitBar.RIGHT = 2;
38160
38161 /**
38162  * Placement constant - The resizing element is positioned above the splitter element
38163  * @static
38164  * @type Number
38165  */
38166 Roo.bootstrap.SplitBar.TOP = 3;
38167
38168 /**
38169  * Placement constant - The resizing element is positioned under splitter element
38170  * @static
38171  * @type Number
38172  */
38173 Roo.bootstrap.SplitBar.BOTTOM = 4;
38174 Roo.namespace("Roo.bootstrap.layout");/*
38175  * Based on:
38176  * Ext JS Library 1.1.1
38177  * Copyright(c) 2006-2007, Ext JS, LLC.
38178  *
38179  * Originally Released Under LGPL - original licence link has changed is not relivant.
38180  *
38181  * Fork - LGPL
38182  * <script type="text/javascript">
38183  */
38184
38185 /**
38186  * @class Roo.bootstrap.layout.Manager
38187  * @extends Roo.bootstrap.Component
38188  * Base class for layout managers.
38189  */
38190 Roo.bootstrap.layout.Manager = function(config)
38191 {
38192     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38193
38194
38195
38196
38197
38198     /** false to disable window resize monitoring @type Boolean */
38199     this.monitorWindowResize = true;
38200     this.regions = {};
38201     this.addEvents({
38202         /**
38203          * @event layout
38204          * Fires when a layout is performed.
38205          * @param {Roo.LayoutManager} this
38206          */
38207         "layout" : true,
38208         /**
38209          * @event regionresized
38210          * Fires when the user resizes a region.
38211          * @param {Roo.LayoutRegion} region The resized region
38212          * @param {Number} newSize The new size (width for east/west, height for north/south)
38213          */
38214         "regionresized" : true,
38215         /**
38216          * @event regioncollapsed
38217          * Fires when a region is collapsed.
38218          * @param {Roo.LayoutRegion} region The collapsed region
38219          */
38220         "regioncollapsed" : true,
38221         /**
38222          * @event regionexpanded
38223          * Fires when a region is expanded.
38224          * @param {Roo.LayoutRegion} region The expanded region
38225          */
38226         "regionexpanded" : true
38227     });
38228     this.updating = false;
38229
38230     if (config.el) {
38231         this.el = Roo.get(config.el);
38232         this.initEvents();
38233     }
38234
38235 };
38236
38237 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38238
38239
38240     regions : null,
38241
38242     monitorWindowResize : true,
38243
38244
38245     updating : false,
38246
38247
38248     onRender : function(ct, position)
38249     {
38250         if(!this.el){
38251             this.el = Roo.get(ct);
38252             this.initEvents();
38253         }
38254         //this.fireEvent('render',this);
38255     },
38256
38257
38258     initEvents: function()
38259     {
38260
38261
38262         // ie scrollbar fix
38263         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38264             document.body.scroll = "no";
38265         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38266             this.el.position('relative');
38267         }
38268         this.id = this.el.id;
38269         this.el.addClass("roo-layout-container");
38270         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38271         if(this.el.dom != document.body ) {
38272             this.el.on('resize', this.layout,this);
38273             this.el.on('show', this.layout,this);
38274         }
38275
38276     },
38277
38278     /**
38279      * Returns true if this layout is currently being updated
38280      * @return {Boolean}
38281      */
38282     isUpdating : function(){
38283         return this.updating;
38284     },
38285
38286     /**
38287      * Suspend the LayoutManager from doing auto-layouts while
38288      * making multiple add or remove calls
38289      */
38290     beginUpdate : function(){
38291         this.updating = true;
38292     },
38293
38294     /**
38295      * Restore auto-layouts and optionally disable the manager from performing a layout
38296      * @param {Boolean} noLayout true to disable a layout update
38297      */
38298     endUpdate : function(noLayout){
38299         this.updating = false;
38300         if(!noLayout){
38301             this.layout();
38302         }
38303     },
38304
38305     layout: function(){
38306         // abstract...
38307     },
38308
38309     onRegionResized : function(region, newSize){
38310         this.fireEvent("regionresized", region, newSize);
38311         this.layout();
38312     },
38313
38314     onRegionCollapsed : function(region){
38315         this.fireEvent("regioncollapsed", region);
38316     },
38317
38318     onRegionExpanded : function(region){
38319         this.fireEvent("regionexpanded", region);
38320     },
38321
38322     /**
38323      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38324      * performs box-model adjustments.
38325      * @return {Object} The size as an object {width: (the width), height: (the height)}
38326      */
38327     getViewSize : function()
38328     {
38329         var size;
38330         if(this.el.dom != document.body){
38331             size = this.el.getSize();
38332         }else{
38333             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38334         }
38335         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38336         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38337         return size;
38338     },
38339
38340     /**
38341      * Returns the Element this layout is bound to.
38342      * @return {Roo.Element}
38343      */
38344     getEl : function(){
38345         return this.el;
38346     },
38347
38348     /**
38349      * Returns the specified region.
38350      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38351      * @return {Roo.LayoutRegion}
38352      */
38353     getRegion : function(target){
38354         return this.regions[target.toLowerCase()];
38355     },
38356
38357     onWindowResize : function(){
38358         if(this.monitorWindowResize){
38359             this.layout();
38360         }
38361     }
38362 });
38363 /*
38364  * Based on:
38365  * Ext JS Library 1.1.1
38366  * Copyright(c) 2006-2007, Ext JS, LLC.
38367  *
38368  * Originally Released Under LGPL - original licence link has changed is not relivant.
38369  *
38370  * Fork - LGPL
38371  * <script type="text/javascript">
38372  */
38373 /**
38374  * @class Roo.bootstrap.layout.Border
38375  * @extends Roo.bootstrap.layout.Manager
38376  * @builder-top
38377  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38378  * please see: examples/bootstrap/nested.html<br><br>
38379  
38380 <b>The container the layout is rendered into can be either the body element or any other element.
38381 If it is not the body element, the container needs to either be an absolute positioned element,
38382 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38383 the container size if it is not the body element.</b>
38384
38385 * @constructor
38386 * Create a new Border
38387 * @param {Object} config Configuration options
38388  */
38389 Roo.bootstrap.layout.Border = function(config){
38390     config = config || {};
38391     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38392     
38393     
38394     
38395     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38396         if(config[region]){
38397             config[region].region = region;
38398             this.addRegion(config[region]);
38399         }
38400     },this);
38401     
38402 };
38403
38404 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38405
38406 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38407     
38408     parent : false, // this might point to a 'nest' or a ???
38409     
38410     /**
38411      * Creates and adds a new region if it doesn't already exist.
38412      * @param {String} target The target region key (north, south, east, west or center).
38413      * @param {Object} config The regions config object
38414      * @return {BorderLayoutRegion} The new region
38415      */
38416     addRegion : function(config)
38417     {
38418         if(!this.regions[config.region]){
38419             var r = this.factory(config);
38420             this.bindRegion(r);
38421         }
38422         return this.regions[config.region];
38423     },
38424
38425     // private (kinda)
38426     bindRegion : function(r){
38427         this.regions[r.config.region] = r;
38428         
38429         r.on("visibilitychange",    this.layout, this);
38430         r.on("paneladded",          this.layout, this);
38431         r.on("panelremoved",        this.layout, this);
38432         r.on("invalidated",         this.layout, this);
38433         r.on("resized",             this.onRegionResized, this);
38434         r.on("collapsed",           this.onRegionCollapsed, this);
38435         r.on("expanded",            this.onRegionExpanded, this);
38436     },
38437
38438     /**
38439      * Performs a layout update.
38440      */
38441     layout : function()
38442     {
38443         if(this.updating) {
38444             return;
38445         }
38446         
38447         // render all the rebions if they have not been done alreayd?
38448         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38449             if(this.regions[region] && !this.regions[region].bodyEl){
38450                 this.regions[region].onRender(this.el)
38451             }
38452         },this);
38453         
38454         var size = this.getViewSize();
38455         var w = size.width;
38456         var h = size.height;
38457         var centerW = w;
38458         var centerH = h;
38459         var centerY = 0;
38460         var centerX = 0;
38461         //var x = 0, y = 0;
38462
38463         var rs = this.regions;
38464         var north = rs["north"];
38465         var south = rs["south"]; 
38466         var west = rs["west"];
38467         var east = rs["east"];
38468         var center = rs["center"];
38469         //if(this.hideOnLayout){ // not supported anymore
38470             //c.el.setStyle("display", "none");
38471         //}
38472         if(north && north.isVisible()){
38473             var b = north.getBox();
38474             var m = north.getMargins();
38475             b.width = w - (m.left+m.right);
38476             b.x = m.left;
38477             b.y = m.top;
38478             centerY = b.height + b.y + m.bottom;
38479             centerH -= centerY;
38480             north.updateBox(this.safeBox(b));
38481         }
38482         if(south && south.isVisible()){
38483             var b = south.getBox();
38484             var m = south.getMargins();
38485             b.width = w - (m.left+m.right);
38486             b.x = m.left;
38487             var totalHeight = (b.height + m.top + m.bottom);
38488             b.y = h - totalHeight + m.top;
38489             centerH -= totalHeight;
38490             south.updateBox(this.safeBox(b));
38491         }
38492         if(west && west.isVisible()){
38493             var b = west.getBox();
38494             var m = west.getMargins();
38495             b.height = centerH - (m.top+m.bottom);
38496             b.x = m.left;
38497             b.y = centerY + m.top;
38498             var totalWidth = (b.width + m.left + m.right);
38499             centerX += totalWidth;
38500             centerW -= totalWidth;
38501             west.updateBox(this.safeBox(b));
38502         }
38503         if(east && east.isVisible()){
38504             var b = east.getBox();
38505             var m = east.getMargins();
38506             b.height = centerH - (m.top+m.bottom);
38507             var totalWidth = (b.width + m.left + m.right);
38508             b.x = w - totalWidth + m.left;
38509             b.y = centerY + m.top;
38510             centerW -= totalWidth;
38511             east.updateBox(this.safeBox(b));
38512         }
38513         if(center){
38514             var m = center.getMargins();
38515             var centerBox = {
38516                 x: centerX + m.left,
38517                 y: centerY + m.top,
38518                 width: centerW - (m.left+m.right),
38519                 height: centerH - (m.top+m.bottom)
38520             };
38521             //if(this.hideOnLayout){
38522                 //center.el.setStyle("display", "block");
38523             //}
38524             center.updateBox(this.safeBox(centerBox));
38525         }
38526         this.el.repaint();
38527         this.fireEvent("layout", this);
38528     },
38529
38530     // private
38531     safeBox : function(box){
38532         box.width = Math.max(0, box.width);
38533         box.height = Math.max(0, box.height);
38534         return box;
38535     },
38536
38537     /**
38538      * Adds a ContentPanel (or subclass) to this layout.
38539      * @param {String} target The target region key (north, south, east, west or center).
38540      * @param {Roo.ContentPanel} panel The panel to add
38541      * @return {Roo.ContentPanel} The added panel
38542      */
38543     add : function(target, panel){
38544          
38545         target = target.toLowerCase();
38546         return this.regions[target].add(panel);
38547     },
38548
38549     /**
38550      * Remove a ContentPanel (or subclass) to this layout.
38551      * @param {String} target The target region key (north, south, east, west or center).
38552      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38553      * @return {Roo.ContentPanel} The removed panel
38554      */
38555     remove : function(target, panel){
38556         target = target.toLowerCase();
38557         return this.regions[target].remove(panel);
38558     },
38559
38560     /**
38561      * Searches all regions for a panel with the specified id
38562      * @param {String} panelId
38563      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38564      */
38565     findPanel : function(panelId){
38566         var rs = this.regions;
38567         for(var target in rs){
38568             if(typeof rs[target] != "function"){
38569                 var p = rs[target].getPanel(panelId);
38570                 if(p){
38571                     return p;
38572                 }
38573             }
38574         }
38575         return null;
38576     },
38577
38578     /**
38579      * Searches all regions for a panel with the specified id and activates (shows) it.
38580      * @param {String/ContentPanel} panelId The panels id or the panel itself
38581      * @return {Roo.ContentPanel} The shown panel or null
38582      */
38583     showPanel : function(panelId) {
38584       var rs = this.regions;
38585       for(var target in rs){
38586          var r = rs[target];
38587          if(typeof r != "function"){
38588             if(r.hasPanel(panelId)){
38589                return r.showPanel(panelId);
38590             }
38591          }
38592       }
38593       return null;
38594    },
38595
38596    /**
38597      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38598      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38599      */
38600    /*
38601     restoreState : function(provider){
38602         if(!provider){
38603             provider = Roo.state.Manager;
38604         }
38605         var sm = new Roo.LayoutStateManager();
38606         sm.init(this, provider);
38607     },
38608 */
38609  
38610  
38611     /**
38612      * Adds a xtype elements to the layout.
38613      * <pre><code>
38614
38615 layout.addxtype({
38616        xtype : 'ContentPanel',
38617        region: 'west',
38618        items: [ .... ]
38619    }
38620 );
38621
38622 layout.addxtype({
38623         xtype : 'NestedLayoutPanel',
38624         region: 'west',
38625         layout: {
38626            center: { },
38627            west: { }   
38628         },
38629         items : [ ... list of content panels or nested layout panels.. ]
38630    }
38631 );
38632 </code></pre>
38633      * @param {Object} cfg Xtype definition of item to add.
38634      */
38635     addxtype : function(cfg)
38636     {
38637         // basically accepts a pannel...
38638         // can accept a layout region..!?!?
38639         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38640         
38641         
38642         // theory?  children can only be panels??
38643         
38644         //if (!cfg.xtype.match(/Panel$/)) {
38645         //    return false;
38646         //}
38647         var ret = false;
38648         
38649         if (typeof(cfg.region) == 'undefined') {
38650             Roo.log("Failed to add Panel, region was not set");
38651             Roo.log(cfg);
38652             return false;
38653         }
38654         var region = cfg.region;
38655         delete cfg.region;
38656         
38657           
38658         var xitems = [];
38659         if (cfg.items) {
38660             xitems = cfg.items;
38661             delete cfg.items;
38662         }
38663         var nb = false;
38664         
38665         if ( region == 'center') {
38666             Roo.log("Center: " + cfg.title);
38667         }
38668         
38669         
38670         switch(cfg.xtype) 
38671         {
38672             case 'Content':  // ContentPanel (el, cfg)
38673             case 'Scroll':  // ContentPanel (el, cfg)
38674             case 'View': 
38675                 cfg.autoCreate = cfg.autoCreate || true;
38676                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38677                 //} else {
38678                 //    var el = this.el.createChild();
38679                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38680                 //}
38681                 
38682                 this.add(region, ret);
38683                 break;
38684             
38685             /*
38686             case 'TreePanel': // our new panel!
38687                 cfg.el = this.el.createChild();
38688                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38689                 this.add(region, ret);
38690                 break;
38691             */
38692             
38693             case 'Nest': 
38694                 // create a new Layout (which is  a Border Layout...
38695                 
38696                 var clayout = cfg.layout;
38697                 clayout.el  = this.el.createChild();
38698                 clayout.items   = clayout.items  || [];
38699                 
38700                 delete cfg.layout;
38701                 
38702                 // replace this exitems with the clayout ones..
38703                 xitems = clayout.items;
38704                  
38705                 // force background off if it's in center...
38706                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38707                     cfg.background = false;
38708                 }
38709                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38710                 
38711                 
38712                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38713                 //console.log('adding nested layout panel '  + cfg.toSource());
38714                 this.add(region, ret);
38715                 nb = {}; /// find first...
38716                 break;
38717             
38718             case 'Grid':
38719                 
38720                 // needs grid and region
38721                 
38722                 //var el = this.getRegion(region).el.createChild();
38723                 /*
38724                  *var el = this.el.createChild();
38725                 // create the grid first...
38726                 cfg.grid.container = el;
38727                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38728                 */
38729                 
38730                 if (region == 'center' && this.active ) {
38731                     cfg.background = false;
38732                 }
38733                 
38734                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38735                 
38736                 this.add(region, ret);
38737                 /*
38738                 if (cfg.background) {
38739                     // render grid on panel activation (if panel background)
38740                     ret.on('activate', function(gp) {
38741                         if (!gp.grid.rendered) {
38742                     //        gp.grid.render(el);
38743                         }
38744                     });
38745                 } else {
38746                   //  cfg.grid.render(el);
38747                 }
38748                 */
38749                 break;
38750            
38751            
38752             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38753                 // it was the old xcomponent building that caused this before.
38754                 // espeically if border is the top element in the tree.
38755                 ret = this;
38756                 break; 
38757                 
38758                     
38759                 
38760                 
38761                 
38762             default:
38763                 /*
38764                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38765                     
38766                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38767                     this.add(region, ret);
38768                 } else {
38769                 */
38770                     Roo.log(cfg);
38771                     throw "Can not add '" + cfg.xtype + "' to Border";
38772                     return null;
38773              
38774                                 
38775              
38776         }
38777         this.beginUpdate();
38778         // add children..
38779         var region = '';
38780         var abn = {};
38781         Roo.each(xitems, function(i)  {
38782             region = nb && i.region ? i.region : false;
38783             
38784             var add = ret.addxtype(i);
38785            
38786             if (region) {
38787                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38788                 if (!i.background) {
38789                     abn[region] = nb[region] ;
38790                 }
38791             }
38792             
38793         });
38794         this.endUpdate();
38795
38796         // make the last non-background panel active..
38797         //if (nb) { Roo.log(abn); }
38798         if (nb) {
38799             
38800             for(var r in abn) {
38801                 region = this.getRegion(r);
38802                 if (region) {
38803                     // tried using nb[r], but it does not work..
38804                      
38805                     region.showPanel(abn[r]);
38806                    
38807                 }
38808             }
38809         }
38810         return ret;
38811         
38812     },
38813     
38814     
38815 // private
38816     factory : function(cfg)
38817     {
38818         
38819         var validRegions = Roo.bootstrap.layout.Border.regions;
38820
38821         var target = cfg.region;
38822         cfg.mgr = this;
38823         
38824         var r = Roo.bootstrap.layout;
38825         Roo.log(target);
38826         switch(target){
38827             case "north":
38828                 return new r.North(cfg);
38829             case "south":
38830                 return new r.South(cfg);
38831             case "east":
38832                 return new r.East(cfg);
38833             case "west":
38834                 return new r.West(cfg);
38835             case "center":
38836                 return new r.Center(cfg);
38837         }
38838         throw 'Layout region "'+target+'" not supported.';
38839     }
38840     
38841     
38842 });
38843  /*
38844  * Based on:
38845  * Ext JS Library 1.1.1
38846  * Copyright(c) 2006-2007, Ext JS, LLC.
38847  *
38848  * Originally Released Under LGPL - original licence link has changed is not relivant.
38849  *
38850  * Fork - LGPL
38851  * <script type="text/javascript">
38852  */
38853  
38854 /**
38855  * @class Roo.bootstrap.layout.Basic
38856  * @extends Roo.util.Observable
38857  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38858  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38859  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38860  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38861  * @cfg {string}   region  the region that it inhabits..
38862  * @cfg {bool}   skipConfig skip config?
38863  * 
38864
38865  */
38866 Roo.bootstrap.layout.Basic = function(config){
38867     
38868     this.mgr = config.mgr;
38869     
38870     this.position = config.region;
38871     
38872     var skipConfig = config.skipConfig;
38873     
38874     this.events = {
38875         /**
38876          * @scope Roo.BasicLayoutRegion
38877          */
38878         
38879         /**
38880          * @event beforeremove
38881          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38882          * @param {Roo.LayoutRegion} this
38883          * @param {Roo.ContentPanel} panel The panel
38884          * @param {Object} e The cancel event object
38885          */
38886         "beforeremove" : true,
38887         /**
38888          * @event invalidated
38889          * Fires when the layout for this region is changed.
38890          * @param {Roo.LayoutRegion} this
38891          */
38892         "invalidated" : true,
38893         /**
38894          * @event visibilitychange
38895          * Fires when this region is shown or hidden 
38896          * @param {Roo.LayoutRegion} this
38897          * @param {Boolean} visibility true or false
38898          */
38899         "visibilitychange" : true,
38900         /**
38901          * @event paneladded
38902          * Fires when a panel is added. 
38903          * @param {Roo.LayoutRegion} this
38904          * @param {Roo.ContentPanel} panel The panel
38905          */
38906         "paneladded" : true,
38907         /**
38908          * @event panelremoved
38909          * Fires when a panel is removed. 
38910          * @param {Roo.LayoutRegion} this
38911          * @param {Roo.ContentPanel} panel The panel
38912          */
38913         "panelremoved" : true,
38914         /**
38915          * @event beforecollapse
38916          * Fires when this region before collapse.
38917          * @param {Roo.LayoutRegion} this
38918          */
38919         "beforecollapse" : true,
38920         /**
38921          * @event collapsed
38922          * Fires when this region is collapsed.
38923          * @param {Roo.LayoutRegion} this
38924          */
38925         "collapsed" : true,
38926         /**
38927          * @event expanded
38928          * Fires when this region is expanded.
38929          * @param {Roo.LayoutRegion} this
38930          */
38931         "expanded" : true,
38932         /**
38933          * @event slideshow
38934          * Fires when this region is slid into view.
38935          * @param {Roo.LayoutRegion} this
38936          */
38937         "slideshow" : true,
38938         /**
38939          * @event slidehide
38940          * Fires when this region slides out of view. 
38941          * @param {Roo.LayoutRegion} this
38942          */
38943         "slidehide" : true,
38944         /**
38945          * @event panelactivated
38946          * Fires when a panel is activated. 
38947          * @param {Roo.LayoutRegion} this
38948          * @param {Roo.ContentPanel} panel The activated panel
38949          */
38950         "panelactivated" : true,
38951         /**
38952          * @event resized
38953          * Fires when the user resizes this region. 
38954          * @param {Roo.LayoutRegion} this
38955          * @param {Number} newSize The new size (width for east/west, height for north/south)
38956          */
38957         "resized" : true
38958     };
38959     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38960     this.panels = new Roo.util.MixedCollection();
38961     this.panels.getKey = this.getPanelId.createDelegate(this);
38962     this.box = null;
38963     this.activePanel = null;
38964     // ensure listeners are added...
38965     
38966     if (config.listeners || config.events) {
38967         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38968             listeners : config.listeners || {},
38969             events : config.events || {}
38970         });
38971     }
38972     
38973     if(skipConfig !== true){
38974         this.applyConfig(config);
38975     }
38976 };
38977
38978 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38979 {
38980     getPanelId : function(p){
38981         return p.getId();
38982     },
38983     
38984     applyConfig : function(config){
38985         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38986         this.config = config;
38987         
38988     },
38989     
38990     /**
38991      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38992      * the width, for horizontal (north, south) the height.
38993      * @param {Number} newSize The new width or height
38994      */
38995     resizeTo : function(newSize){
38996         var el = this.el ? this.el :
38997                  (this.activePanel ? this.activePanel.getEl() : null);
38998         if(el){
38999             switch(this.position){
39000                 case "east":
39001                 case "west":
39002                     el.setWidth(newSize);
39003                     this.fireEvent("resized", this, newSize);
39004                 break;
39005                 case "north":
39006                 case "south":
39007                     el.setHeight(newSize);
39008                     this.fireEvent("resized", this, newSize);
39009                 break;                
39010             }
39011         }
39012     },
39013     
39014     getBox : function(){
39015         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39016     },
39017     
39018     getMargins : function(){
39019         return this.margins;
39020     },
39021     
39022     updateBox : function(box){
39023         this.box = box;
39024         var el = this.activePanel.getEl();
39025         el.dom.style.left = box.x + "px";
39026         el.dom.style.top = box.y + "px";
39027         this.activePanel.setSize(box.width, box.height);
39028     },
39029     
39030     /**
39031      * Returns the container element for this region.
39032      * @return {Roo.Element}
39033      */
39034     getEl : function(){
39035         return this.activePanel;
39036     },
39037     
39038     /**
39039      * Returns true if this region is currently visible.
39040      * @return {Boolean}
39041      */
39042     isVisible : function(){
39043         return this.activePanel ? true : false;
39044     },
39045     
39046     setActivePanel : function(panel){
39047         panel = this.getPanel(panel);
39048         if(this.activePanel && this.activePanel != panel){
39049             this.activePanel.setActiveState(false);
39050             this.activePanel.getEl().setLeftTop(-10000,-10000);
39051         }
39052         this.activePanel = panel;
39053         panel.setActiveState(true);
39054         if(this.box){
39055             panel.setSize(this.box.width, this.box.height);
39056         }
39057         this.fireEvent("panelactivated", this, panel);
39058         this.fireEvent("invalidated");
39059     },
39060     
39061     /**
39062      * Show the specified panel.
39063      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39064      * @return {Roo.ContentPanel} The shown panel or null
39065      */
39066     showPanel : function(panel){
39067         panel = this.getPanel(panel);
39068         if(panel){
39069             this.setActivePanel(panel);
39070         }
39071         return panel;
39072     },
39073     
39074     /**
39075      * Get the active panel for this region.
39076      * @return {Roo.ContentPanel} The active panel or null
39077      */
39078     getActivePanel : function(){
39079         return this.activePanel;
39080     },
39081     
39082     /**
39083      * Add the passed ContentPanel(s)
39084      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39085      * @return {Roo.ContentPanel} The panel added (if only one was added)
39086      */
39087     add : function(panel){
39088         if(arguments.length > 1){
39089             for(var i = 0, len = arguments.length; i < len; i++) {
39090                 this.add(arguments[i]);
39091             }
39092             return null;
39093         }
39094         if(this.hasPanel(panel)){
39095             this.showPanel(panel);
39096             return panel;
39097         }
39098         var el = panel.getEl();
39099         if(el.dom.parentNode != this.mgr.el.dom){
39100             this.mgr.el.dom.appendChild(el.dom);
39101         }
39102         if(panel.setRegion){
39103             panel.setRegion(this);
39104         }
39105         this.panels.add(panel);
39106         el.setStyle("position", "absolute");
39107         if(!panel.background){
39108             this.setActivePanel(panel);
39109             if(this.config.initialSize && this.panels.getCount()==1){
39110                 this.resizeTo(this.config.initialSize);
39111             }
39112         }
39113         this.fireEvent("paneladded", this, panel);
39114         return panel;
39115     },
39116     
39117     /**
39118      * Returns true if the panel is in this region.
39119      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39120      * @return {Boolean}
39121      */
39122     hasPanel : function(panel){
39123         if(typeof panel == "object"){ // must be panel obj
39124             panel = panel.getId();
39125         }
39126         return this.getPanel(panel) ? true : false;
39127     },
39128     
39129     /**
39130      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39131      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39132      * @param {Boolean} preservePanel Overrides the config preservePanel option
39133      * @return {Roo.ContentPanel} The panel that was removed
39134      */
39135     remove : function(panel, preservePanel){
39136         panel = this.getPanel(panel);
39137         if(!panel){
39138             return null;
39139         }
39140         var e = {};
39141         this.fireEvent("beforeremove", this, panel, e);
39142         if(e.cancel === true){
39143             return null;
39144         }
39145         var panelId = panel.getId();
39146         this.panels.removeKey(panelId);
39147         return panel;
39148     },
39149     
39150     /**
39151      * Returns the panel specified or null if it's not in this region.
39152      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39153      * @return {Roo.ContentPanel}
39154      */
39155     getPanel : function(id){
39156         if(typeof id == "object"){ // must be panel obj
39157             return id;
39158         }
39159         return this.panels.get(id);
39160     },
39161     
39162     /**
39163      * Returns this regions position (north/south/east/west/center).
39164      * @return {String} 
39165      */
39166     getPosition: function(){
39167         return this.position;    
39168     }
39169 });/*
39170  * Based on:
39171  * Ext JS Library 1.1.1
39172  * Copyright(c) 2006-2007, Ext JS, LLC.
39173  *
39174  * Originally Released Under LGPL - original licence link has changed is not relivant.
39175  *
39176  * Fork - LGPL
39177  * <script type="text/javascript">
39178  */
39179  
39180 /**
39181  * @class Roo.bootstrap.layout.Region
39182  * @extends Roo.bootstrap.layout.Basic
39183  * This class represents a region in a layout manager.
39184  
39185  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39186  * @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})
39187  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39188  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39189  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39190  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39191  * @cfg {String}    title           The title for the region (overrides panel titles)
39192  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39193  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39194  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39195  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39196  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39197  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39198  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39199  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39200  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39201  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39202
39203  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39204  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39205  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39206  * @cfg {Number}    width           For East/West panels
39207  * @cfg {Number}    height          For North/South panels
39208  * @cfg {Boolean}   split           To show the splitter
39209  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39210  * 
39211  * @cfg {string}   cls             Extra CSS classes to add to region
39212  * 
39213  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39214  * @cfg {string}   region  the region that it inhabits..
39215  *
39216
39217  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39218  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39219
39220  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39221  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39222  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39223  */
39224 Roo.bootstrap.layout.Region = function(config)
39225 {
39226     this.applyConfig(config);
39227
39228     var mgr = config.mgr;
39229     var pos = config.region;
39230     config.skipConfig = true;
39231     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39232     
39233     if (mgr.el) {
39234         this.onRender(mgr.el);   
39235     }
39236      
39237     this.visible = true;
39238     this.collapsed = false;
39239     this.unrendered_panels = [];
39240 };
39241
39242 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39243
39244     position: '', // set by wrapper (eg. north/south etc..)
39245     unrendered_panels : null,  // unrendered panels.
39246     
39247     tabPosition : false,
39248     
39249     mgr: false, // points to 'Border'
39250     
39251     
39252     createBody : function(){
39253         /** This region's body element 
39254         * @type Roo.Element */
39255         this.bodyEl = this.el.createChild({
39256                 tag: "div",
39257                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39258         });
39259     },
39260
39261     onRender: function(ctr, pos)
39262     {
39263         var dh = Roo.DomHelper;
39264         /** This region's container element 
39265         * @type Roo.Element */
39266         this.el = dh.append(ctr.dom, {
39267                 tag: "div",
39268                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39269             }, true);
39270         /** This region's title element 
39271         * @type Roo.Element */
39272     
39273         this.titleEl = dh.append(this.el.dom,  {
39274                 tag: "div",
39275                 unselectable: "on",
39276                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39277                 children:[
39278                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39279                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39280                 ]
39281             }, true);
39282         
39283         this.titleEl.enableDisplayMode();
39284         /** This region's title text element 
39285         * @type HTMLElement */
39286         this.titleTextEl = this.titleEl.dom.firstChild;
39287         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39288         /*
39289         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39290         this.closeBtn.enableDisplayMode();
39291         this.closeBtn.on("click", this.closeClicked, this);
39292         this.closeBtn.hide();
39293     */
39294         this.createBody(this.config);
39295         if(this.config.hideWhenEmpty){
39296             this.hide();
39297             this.on("paneladded", this.validateVisibility, this);
39298             this.on("panelremoved", this.validateVisibility, this);
39299         }
39300         if(this.autoScroll){
39301             this.bodyEl.setStyle("overflow", "auto");
39302         }else{
39303             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39304         }
39305         //if(c.titlebar !== false){
39306             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39307                 this.titleEl.hide();
39308             }else{
39309                 this.titleEl.show();
39310                 if(this.config.title){
39311                     this.titleTextEl.innerHTML = this.config.title;
39312                 }
39313             }
39314         //}
39315         if(this.config.collapsed){
39316             this.collapse(true);
39317         }
39318         if(this.config.hidden){
39319             this.hide();
39320         }
39321         
39322         if (this.unrendered_panels && this.unrendered_panels.length) {
39323             for (var i =0;i< this.unrendered_panels.length; i++) {
39324                 this.add(this.unrendered_panels[i]);
39325             }
39326             this.unrendered_panels = null;
39327             
39328         }
39329         
39330     },
39331     
39332     applyConfig : function(c)
39333     {
39334         /*
39335          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39336             var dh = Roo.DomHelper;
39337             if(c.titlebar !== false){
39338                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39339                 this.collapseBtn.on("click", this.collapse, this);
39340                 this.collapseBtn.enableDisplayMode();
39341                 /*
39342                 if(c.showPin === true || this.showPin){
39343                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39344                     this.stickBtn.enableDisplayMode();
39345                     this.stickBtn.on("click", this.expand, this);
39346                     this.stickBtn.hide();
39347                 }
39348                 
39349             }
39350             */
39351             /** This region's collapsed element
39352             * @type Roo.Element */
39353             /*
39354              *
39355             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39356                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39357             ]}, true);
39358             
39359             if(c.floatable !== false){
39360                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39361                this.collapsedEl.on("click", this.collapseClick, this);
39362             }
39363
39364             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39365                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39366                    id: "message", unselectable: "on", style:{"float":"left"}});
39367                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39368              }
39369             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39370             this.expandBtn.on("click", this.expand, this);
39371             
39372         }
39373         
39374         if(this.collapseBtn){
39375             this.collapseBtn.setVisible(c.collapsible == true);
39376         }
39377         
39378         this.cmargins = c.cmargins || this.cmargins ||
39379                          (this.position == "west" || this.position == "east" ?
39380                              {top: 0, left: 2, right:2, bottom: 0} :
39381                              {top: 2, left: 0, right:0, bottom: 2});
39382         */
39383         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39384         
39385         
39386         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39387         
39388         this.autoScroll = c.autoScroll || false;
39389         
39390         
39391        
39392         
39393         this.duration = c.duration || .30;
39394         this.slideDuration = c.slideDuration || .45;
39395         this.config = c;
39396        
39397     },
39398     /**
39399      * Returns true if this region is currently visible.
39400      * @return {Boolean}
39401      */
39402     isVisible : function(){
39403         return this.visible;
39404     },
39405
39406     /**
39407      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39408      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39409      */
39410     //setCollapsedTitle : function(title){
39411     //    title = title || "&#160;";
39412      //   if(this.collapsedTitleTextEl){
39413       //      this.collapsedTitleTextEl.innerHTML = title;
39414        // }
39415     //},
39416
39417     getBox : function(){
39418         var b;
39419       //  if(!this.collapsed){
39420             b = this.el.getBox(false, true);
39421        // }else{
39422           //  b = this.collapsedEl.getBox(false, true);
39423         //}
39424         return b;
39425     },
39426
39427     getMargins : function(){
39428         return this.margins;
39429         //return this.collapsed ? this.cmargins : this.margins;
39430     },
39431 /*
39432     highlight : function(){
39433         this.el.addClass("x-layout-panel-dragover");
39434     },
39435
39436     unhighlight : function(){
39437         this.el.removeClass("x-layout-panel-dragover");
39438     },
39439 */
39440     updateBox : function(box)
39441     {
39442         if (!this.bodyEl) {
39443             return; // not rendered yet..
39444         }
39445         
39446         this.box = box;
39447         if(!this.collapsed){
39448             this.el.dom.style.left = box.x + "px";
39449             this.el.dom.style.top = box.y + "px";
39450             this.updateBody(box.width, box.height);
39451         }else{
39452             this.collapsedEl.dom.style.left = box.x + "px";
39453             this.collapsedEl.dom.style.top = box.y + "px";
39454             this.collapsedEl.setSize(box.width, box.height);
39455         }
39456         if(this.tabs){
39457             this.tabs.autoSizeTabs();
39458         }
39459     },
39460
39461     updateBody : function(w, h)
39462     {
39463         if(w !== null){
39464             this.el.setWidth(w);
39465             w -= this.el.getBorderWidth("rl");
39466             if(this.config.adjustments){
39467                 w += this.config.adjustments[0];
39468             }
39469         }
39470         if(h !== null && h > 0){
39471             this.el.setHeight(h);
39472             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39473             h -= this.el.getBorderWidth("tb");
39474             if(this.config.adjustments){
39475                 h += this.config.adjustments[1];
39476             }
39477             this.bodyEl.setHeight(h);
39478             if(this.tabs){
39479                 h = this.tabs.syncHeight(h);
39480             }
39481         }
39482         if(this.panelSize){
39483             w = w !== null ? w : this.panelSize.width;
39484             h = h !== null ? h : this.panelSize.height;
39485         }
39486         if(this.activePanel){
39487             var el = this.activePanel.getEl();
39488             w = w !== null ? w : el.getWidth();
39489             h = h !== null ? h : el.getHeight();
39490             this.panelSize = {width: w, height: h};
39491             this.activePanel.setSize(w, h);
39492         }
39493         if(Roo.isIE && this.tabs){
39494             this.tabs.el.repaint();
39495         }
39496     },
39497
39498     /**
39499      * Returns the container element for this region.
39500      * @return {Roo.Element}
39501      */
39502     getEl : function(){
39503         return this.el;
39504     },
39505
39506     /**
39507      * Hides this region.
39508      */
39509     hide : function(){
39510         //if(!this.collapsed){
39511             this.el.dom.style.left = "-2000px";
39512             this.el.hide();
39513         //}else{
39514          //   this.collapsedEl.dom.style.left = "-2000px";
39515          //   this.collapsedEl.hide();
39516        // }
39517         this.visible = false;
39518         this.fireEvent("visibilitychange", this, false);
39519     },
39520
39521     /**
39522      * Shows this region if it was previously hidden.
39523      */
39524     show : function(){
39525         //if(!this.collapsed){
39526             this.el.show();
39527         //}else{
39528         //    this.collapsedEl.show();
39529        // }
39530         this.visible = true;
39531         this.fireEvent("visibilitychange", this, true);
39532     },
39533 /*
39534     closeClicked : function(){
39535         if(this.activePanel){
39536             this.remove(this.activePanel);
39537         }
39538     },
39539
39540     collapseClick : function(e){
39541         if(this.isSlid){
39542            e.stopPropagation();
39543            this.slideIn();
39544         }else{
39545            e.stopPropagation();
39546            this.slideOut();
39547         }
39548     },
39549 */
39550     /**
39551      * Collapses this region.
39552      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39553      */
39554     /*
39555     collapse : function(skipAnim, skipCheck = false){
39556         if(this.collapsed) {
39557             return;
39558         }
39559         
39560         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39561             
39562             this.collapsed = true;
39563             if(this.split){
39564                 this.split.el.hide();
39565             }
39566             if(this.config.animate && skipAnim !== true){
39567                 this.fireEvent("invalidated", this);
39568                 this.animateCollapse();
39569             }else{
39570                 this.el.setLocation(-20000,-20000);
39571                 this.el.hide();
39572                 this.collapsedEl.show();
39573                 this.fireEvent("collapsed", this);
39574                 this.fireEvent("invalidated", this);
39575             }
39576         }
39577         
39578     },
39579 */
39580     animateCollapse : function(){
39581         // overridden
39582     },
39583
39584     /**
39585      * Expands this region if it was previously collapsed.
39586      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39587      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39588      */
39589     /*
39590     expand : function(e, skipAnim){
39591         if(e) {
39592             e.stopPropagation();
39593         }
39594         if(!this.collapsed || this.el.hasActiveFx()) {
39595             return;
39596         }
39597         if(this.isSlid){
39598             this.afterSlideIn();
39599             skipAnim = true;
39600         }
39601         this.collapsed = false;
39602         if(this.config.animate && skipAnim !== true){
39603             this.animateExpand();
39604         }else{
39605             this.el.show();
39606             if(this.split){
39607                 this.split.el.show();
39608             }
39609             this.collapsedEl.setLocation(-2000,-2000);
39610             this.collapsedEl.hide();
39611             this.fireEvent("invalidated", this);
39612             this.fireEvent("expanded", this);
39613         }
39614     },
39615 */
39616     animateExpand : function(){
39617         // overridden
39618     },
39619
39620     initTabs : function()
39621     {
39622         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39623         
39624         var ts = new Roo.bootstrap.panel.Tabs({
39625             el: this.bodyEl.dom,
39626             region : this,
39627             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39628             disableTooltips: this.config.disableTabTips,
39629             toolbar : this.config.toolbar
39630         });
39631         
39632         if(this.config.hideTabs){
39633             ts.stripWrap.setDisplayed(false);
39634         }
39635         this.tabs = ts;
39636         ts.resizeTabs = this.config.resizeTabs === true;
39637         ts.minTabWidth = this.config.minTabWidth || 40;
39638         ts.maxTabWidth = this.config.maxTabWidth || 250;
39639         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39640         ts.monitorResize = false;
39641         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39642         ts.bodyEl.addClass('roo-layout-tabs-body');
39643         this.panels.each(this.initPanelAsTab, this);
39644     },
39645
39646     initPanelAsTab : function(panel){
39647         var ti = this.tabs.addTab(
39648             panel.getEl().id,
39649             panel.getTitle(),
39650             null,
39651             this.config.closeOnTab && panel.isClosable(),
39652             panel.tpl
39653         );
39654         if(panel.tabTip !== undefined){
39655             ti.setTooltip(panel.tabTip);
39656         }
39657         ti.on("activate", function(){
39658               this.setActivePanel(panel);
39659         }, this);
39660         
39661         if(this.config.closeOnTab){
39662             ti.on("beforeclose", function(t, e){
39663                 e.cancel = true;
39664                 this.remove(panel);
39665             }, this);
39666         }
39667         
39668         panel.tabItem = ti;
39669         
39670         return ti;
39671     },
39672
39673     updatePanelTitle : function(panel, title)
39674     {
39675         if(this.activePanel == panel){
39676             this.updateTitle(title);
39677         }
39678         if(this.tabs){
39679             var ti = this.tabs.getTab(panel.getEl().id);
39680             ti.setText(title);
39681             if(panel.tabTip !== undefined){
39682                 ti.setTooltip(panel.tabTip);
39683             }
39684         }
39685     },
39686
39687     updateTitle : function(title){
39688         if(this.titleTextEl && !this.config.title){
39689             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39690         }
39691     },
39692
39693     setActivePanel : function(panel)
39694     {
39695         panel = this.getPanel(panel);
39696         if(this.activePanel && this.activePanel != panel){
39697             if(this.activePanel.setActiveState(false) === false){
39698                 return;
39699             }
39700         }
39701         this.activePanel = panel;
39702         panel.setActiveState(true);
39703         if(this.panelSize){
39704             panel.setSize(this.panelSize.width, this.panelSize.height);
39705         }
39706         if(this.closeBtn){
39707             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39708         }
39709         this.updateTitle(panel.getTitle());
39710         if(this.tabs){
39711             this.fireEvent("invalidated", this);
39712         }
39713         this.fireEvent("panelactivated", this, panel);
39714     },
39715
39716     /**
39717      * Shows the specified panel.
39718      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39719      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39720      */
39721     showPanel : function(panel)
39722     {
39723         panel = this.getPanel(panel);
39724         if(panel){
39725             if(this.tabs){
39726                 var tab = this.tabs.getTab(panel.getEl().id);
39727                 if(tab.isHidden()){
39728                     this.tabs.unhideTab(tab.id);
39729                 }
39730                 tab.activate();
39731             }else{
39732                 this.setActivePanel(panel);
39733             }
39734         }
39735         return panel;
39736     },
39737
39738     /**
39739      * Get the active panel for this region.
39740      * @return {Roo.ContentPanel} The active panel or null
39741      */
39742     getActivePanel : function(){
39743         return this.activePanel;
39744     },
39745
39746     validateVisibility : function(){
39747         if(this.panels.getCount() < 1){
39748             this.updateTitle("&#160;");
39749             this.closeBtn.hide();
39750             this.hide();
39751         }else{
39752             if(!this.isVisible()){
39753                 this.show();
39754             }
39755         }
39756     },
39757
39758     /**
39759      * Adds the passed ContentPanel(s) to this region.
39760      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39761      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39762      */
39763     add : function(panel)
39764     {
39765         if(arguments.length > 1){
39766             for(var i = 0, len = arguments.length; i < len; i++) {
39767                 this.add(arguments[i]);
39768             }
39769             return null;
39770         }
39771         
39772         // if we have not been rendered yet, then we can not really do much of this..
39773         if (!this.bodyEl) {
39774             this.unrendered_panels.push(panel);
39775             return panel;
39776         }
39777         
39778         
39779         
39780         
39781         if(this.hasPanel(panel)){
39782             this.showPanel(panel);
39783             return panel;
39784         }
39785         panel.setRegion(this);
39786         this.panels.add(panel);
39787        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39788             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39789             // and hide them... ???
39790             this.bodyEl.dom.appendChild(panel.getEl().dom);
39791             if(panel.background !== true){
39792                 this.setActivePanel(panel);
39793             }
39794             this.fireEvent("paneladded", this, panel);
39795             return panel;
39796         }
39797         */
39798         if(!this.tabs){
39799             this.initTabs();
39800         }else{
39801             this.initPanelAsTab(panel);
39802         }
39803         
39804         
39805         if(panel.background !== true){
39806             this.tabs.activate(panel.getEl().id);
39807         }
39808         this.fireEvent("paneladded", this, panel);
39809         return panel;
39810     },
39811
39812     /**
39813      * Hides the tab for the specified panel.
39814      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39815      */
39816     hidePanel : function(panel){
39817         if(this.tabs && (panel = this.getPanel(panel))){
39818             this.tabs.hideTab(panel.getEl().id);
39819         }
39820     },
39821
39822     /**
39823      * Unhides the tab for a previously hidden panel.
39824      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39825      */
39826     unhidePanel : function(panel){
39827         if(this.tabs && (panel = this.getPanel(panel))){
39828             this.tabs.unhideTab(panel.getEl().id);
39829         }
39830     },
39831
39832     clearPanels : function(){
39833         while(this.panels.getCount() > 0){
39834              this.remove(this.panels.first());
39835         }
39836     },
39837
39838     /**
39839      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39840      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39841      * @param {Boolean} preservePanel Overrides the config preservePanel option
39842      * @return {Roo.ContentPanel} The panel that was removed
39843      */
39844     remove : function(panel, preservePanel)
39845     {
39846         panel = this.getPanel(panel);
39847         if(!panel){
39848             return null;
39849         }
39850         var e = {};
39851         this.fireEvent("beforeremove", this, panel, e);
39852         if(e.cancel === true){
39853             return null;
39854         }
39855         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39856         var panelId = panel.getId();
39857         this.panels.removeKey(panelId);
39858         if(preservePanel){
39859             document.body.appendChild(panel.getEl().dom);
39860         }
39861         if(this.tabs){
39862             this.tabs.removeTab(panel.getEl().id);
39863         }else if (!preservePanel){
39864             this.bodyEl.dom.removeChild(panel.getEl().dom);
39865         }
39866         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39867             var p = this.panels.first();
39868             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39869             tempEl.appendChild(p.getEl().dom);
39870             this.bodyEl.update("");
39871             this.bodyEl.dom.appendChild(p.getEl().dom);
39872             tempEl = null;
39873             this.updateTitle(p.getTitle());
39874             this.tabs = null;
39875             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39876             this.setActivePanel(p);
39877         }
39878         panel.setRegion(null);
39879         if(this.activePanel == panel){
39880             this.activePanel = null;
39881         }
39882         if(this.config.autoDestroy !== false && preservePanel !== true){
39883             try{panel.destroy();}catch(e){}
39884         }
39885         this.fireEvent("panelremoved", this, panel);
39886         return panel;
39887     },
39888
39889     /**
39890      * Returns the TabPanel component used by this region
39891      * @return {Roo.TabPanel}
39892      */
39893     getTabs : function(){
39894         return this.tabs;
39895     },
39896
39897     createTool : function(parentEl, className){
39898         var btn = Roo.DomHelper.append(parentEl, {
39899             tag: "div",
39900             cls: "x-layout-tools-button",
39901             children: [ {
39902                 tag: "div",
39903                 cls: "roo-layout-tools-button-inner " + className,
39904                 html: "&#160;"
39905             }]
39906         }, true);
39907         btn.addClassOnOver("roo-layout-tools-button-over");
39908         return btn;
39909     }
39910 });/*
39911  * Based on:
39912  * Ext JS Library 1.1.1
39913  * Copyright(c) 2006-2007, Ext JS, LLC.
39914  *
39915  * Originally Released Under LGPL - original licence link has changed is not relivant.
39916  *
39917  * Fork - LGPL
39918  * <script type="text/javascript">
39919  */
39920  
39921
39922
39923 /**
39924  * @class Roo.SplitLayoutRegion
39925  * @extends Roo.LayoutRegion
39926  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39927  */
39928 Roo.bootstrap.layout.Split = function(config){
39929     this.cursor = config.cursor;
39930     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39931 };
39932
39933 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39934 {
39935     splitTip : "Drag to resize.",
39936     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39937     useSplitTips : false,
39938
39939     applyConfig : function(config){
39940         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39941     },
39942     
39943     onRender : function(ctr,pos) {
39944         
39945         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39946         if(!this.config.split){
39947             return;
39948         }
39949         if(!this.split){
39950             
39951             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39952                             tag: "div",
39953                             id: this.el.id + "-split",
39954                             cls: "roo-layout-split roo-layout-split-"+this.position,
39955                             html: "&#160;"
39956             });
39957             /** The SplitBar for this region 
39958             * @type Roo.SplitBar */
39959             // does not exist yet...
39960             Roo.log([this.position, this.orientation]);
39961             
39962             this.split = new Roo.bootstrap.SplitBar({
39963                 dragElement : splitEl,
39964                 resizingElement: this.el,
39965                 orientation : this.orientation
39966             });
39967             
39968             this.split.on("moved", this.onSplitMove, this);
39969             this.split.useShim = this.config.useShim === true;
39970             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39971             if(this.useSplitTips){
39972                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39973             }
39974             //if(config.collapsible){
39975             //    this.split.el.on("dblclick", this.collapse,  this);
39976             //}
39977         }
39978         if(typeof this.config.minSize != "undefined"){
39979             this.split.minSize = this.config.minSize;
39980         }
39981         if(typeof this.config.maxSize != "undefined"){
39982             this.split.maxSize = this.config.maxSize;
39983         }
39984         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39985             this.hideSplitter();
39986         }
39987         
39988     },
39989
39990     getHMaxSize : function(){
39991          var cmax = this.config.maxSize || 10000;
39992          var center = this.mgr.getRegion("center");
39993          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39994     },
39995
39996     getVMaxSize : function(){
39997          var cmax = this.config.maxSize || 10000;
39998          var center = this.mgr.getRegion("center");
39999          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40000     },
40001
40002     onSplitMove : function(split, newSize){
40003         this.fireEvent("resized", this, newSize);
40004     },
40005     
40006     /** 
40007      * Returns the {@link Roo.SplitBar} for this region.
40008      * @return {Roo.SplitBar}
40009      */
40010     getSplitBar : function(){
40011         return this.split;
40012     },
40013     
40014     hide : function(){
40015         this.hideSplitter();
40016         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40017     },
40018
40019     hideSplitter : function(){
40020         if(this.split){
40021             this.split.el.setLocation(-2000,-2000);
40022             this.split.el.hide();
40023         }
40024     },
40025
40026     show : function(){
40027         if(this.split){
40028             this.split.el.show();
40029         }
40030         Roo.bootstrap.layout.Split.superclass.show.call(this);
40031     },
40032     
40033     beforeSlide: function(){
40034         if(Roo.isGecko){// firefox overflow auto bug workaround
40035             this.bodyEl.clip();
40036             if(this.tabs) {
40037                 this.tabs.bodyEl.clip();
40038             }
40039             if(this.activePanel){
40040                 this.activePanel.getEl().clip();
40041                 
40042                 if(this.activePanel.beforeSlide){
40043                     this.activePanel.beforeSlide();
40044                 }
40045             }
40046         }
40047     },
40048     
40049     afterSlide : function(){
40050         if(Roo.isGecko){// firefox overflow auto bug workaround
40051             this.bodyEl.unclip();
40052             if(this.tabs) {
40053                 this.tabs.bodyEl.unclip();
40054             }
40055             if(this.activePanel){
40056                 this.activePanel.getEl().unclip();
40057                 if(this.activePanel.afterSlide){
40058                     this.activePanel.afterSlide();
40059                 }
40060             }
40061         }
40062     },
40063
40064     initAutoHide : function(){
40065         if(this.autoHide !== false){
40066             if(!this.autoHideHd){
40067                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40068                 this.autoHideHd = {
40069                     "mouseout": function(e){
40070                         if(!e.within(this.el, true)){
40071                             st.delay(500);
40072                         }
40073                     },
40074                     "mouseover" : function(e){
40075                         st.cancel();
40076                     },
40077                     scope : this
40078                 };
40079             }
40080             this.el.on(this.autoHideHd);
40081         }
40082     },
40083
40084     clearAutoHide : function(){
40085         if(this.autoHide !== false){
40086             this.el.un("mouseout", this.autoHideHd.mouseout);
40087             this.el.un("mouseover", this.autoHideHd.mouseover);
40088         }
40089     },
40090
40091     clearMonitor : function(){
40092         Roo.get(document).un("click", this.slideInIf, this);
40093     },
40094
40095     // these names are backwards but not changed for compat
40096     slideOut : function(){
40097         if(this.isSlid || this.el.hasActiveFx()){
40098             return;
40099         }
40100         this.isSlid = true;
40101         if(this.collapseBtn){
40102             this.collapseBtn.hide();
40103         }
40104         this.closeBtnState = this.closeBtn.getStyle('display');
40105         this.closeBtn.hide();
40106         if(this.stickBtn){
40107             this.stickBtn.show();
40108         }
40109         this.el.show();
40110         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40111         this.beforeSlide();
40112         this.el.setStyle("z-index", 10001);
40113         this.el.slideIn(this.getSlideAnchor(), {
40114             callback: function(){
40115                 this.afterSlide();
40116                 this.initAutoHide();
40117                 Roo.get(document).on("click", this.slideInIf, this);
40118                 this.fireEvent("slideshow", this);
40119             },
40120             scope: this,
40121             block: true
40122         });
40123     },
40124
40125     afterSlideIn : function(){
40126         this.clearAutoHide();
40127         this.isSlid = false;
40128         this.clearMonitor();
40129         this.el.setStyle("z-index", "");
40130         if(this.collapseBtn){
40131             this.collapseBtn.show();
40132         }
40133         this.closeBtn.setStyle('display', this.closeBtnState);
40134         if(this.stickBtn){
40135             this.stickBtn.hide();
40136         }
40137         this.fireEvent("slidehide", this);
40138     },
40139
40140     slideIn : function(cb){
40141         if(!this.isSlid || this.el.hasActiveFx()){
40142             Roo.callback(cb);
40143             return;
40144         }
40145         this.isSlid = false;
40146         this.beforeSlide();
40147         this.el.slideOut(this.getSlideAnchor(), {
40148             callback: function(){
40149                 this.el.setLeftTop(-10000, -10000);
40150                 this.afterSlide();
40151                 this.afterSlideIn();
40152                 Roo.callback(cb);
40153             },
40154             scope: this,
40155             block: true
40156         });
40157     },
40158     
40159     slideInIf : function(e){
40160         if(!e.within(this.el)){
40161             this.slideIn();
40162         }
40163     },
40164
40165     animateCollapse : function(){
40166         this.beforeSlide();
40167         this.el.setStyle("z-index", 20000);
40168         var anchor = this.getSlideAnchor();
40169         this.el.slideOut(anchor, {
40170             callback : function(){
40171                 this.el.setStyle("z-index", "");
40172                 this.collapsedEl.slideIn(anchor, {duration:.3});
40173                 this.afterSlide();
40174                 this.el.setLocation(-10000,-10000);
40175                 this.el.hide();
40176                 this.fireEvent("collapsed", this);
40177             },
40178             scope: this,
40179             block: true
40180         });
40181     },
40182
40183     animateExpand : function(){
40184         this.beforeSlide();
40185         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40186         this.el.setStyle("z-index", 20000);
40187         this.collapsedEl.hide({
40188             duration:.1
40189         });
40190         this.el.slideIn(this.getSlideAnchor(), {
40191             callback : function(){
40192                 this.el.setStyle("z-index", "");
40193                 this.afterSlide();
40194                 if(this.split){
40195                     this.split.el.show();
40196                 }
40197                 this.fireEvent("invalidated", this);
40198                 this.fireEvent("expanded", this);
40199             },
40200             scope: this,
40201             block: true
40202         });
40203     },
40204
40205     anchors : {
40206         "west" : "left",
40207         "east" : "right",
40208         "north" : "top",
40209         "south" : "bottom"
40210     },
40211
40212     sanchors : {
40213         "west" : "l",
40214         "east" : "r",
40215         "north" : "t",
40216         "south" : "b"
40217     },
40218
40219     canchors : {
40220         "west" : "tl-tr",
40221         "east" : "tr-tl",
40222         "north" : "tl-bl",
40223         "south" : "bl-tl"
40224     },
40225
40226     getAnchor : function(){
40227         return this.anchors[this.position];
40228     },
40229
40230     getCollapseAnchor : function(){
40231         return this.canchors[this.position];
40232     },
40233
40234     getSlideAnchor : function(){
40235         return this.sanchors[this.position];
40236     },
40237
40238     getAlignAdj : function(){
40239         var cm = this.cmargins;
40240         switch(this.position){
40241             case "west":
40242                 return [0, 0];
40243             break;
40244             case "east":
40245                 return [0, 0];
40246             break;
40247             case "north":
40248                 return [0, 0];
40249             break;
40250             case "south":
40251                 return [0, 0];
40252             break;
40253         }
40254     },
40255
40256     getExpandAdj : function(){
40257         var c = this.collapsedEl, cm = this.cmargins;
40258         switch(this.position){
40259             case "west":
40260                 return [-(cm.right+c.getWidth()+cm.left), 0];
40261             break;
40262             case "east":
40263                 return [cm.right+c.getWidth()+cm.left, 0];
40264             break;
40265             case "north":
40266                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40267             break;
40268             case "south":
40269                 return [0, cm.top+cm.bottom+c.getHeight()];
40270             break;
40271         }
40272     }
40273 });/*
40274  * Based on:
40275  * Ext JS Library 1.1.1
40276  * Copyright(c) 2006-2007, Ext JS, LLC.
40277  *
40278  * Originally Released Under LGPL - original licence link has changed is not relivant.
40279  *
40280  * Fork - LGPL
40281  * <script type="text/javascript">
40282  */
40283 /*
40284  * These classes are private internal classes
40285  */
40286 Roo.bootstrap.layout.Center = function(config){
40287     config.region = "center";
40288     Roo.bootstrap.layout.Region.call(this, config);
40289     this.visible = true;
40290     this.minWidth = config.minWidth || 20;
40291     this.minHeight = config.minHeight || 20;
40292 };
40293
40294 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40295     hide : function(){
40296         // center panel can't be hidden
40297     },
40298     
40299     show : function(){
40300         // center panel can't be hidden
40301     },
40302     
40303     getMinWidth: function(){
40304         return this.minWidth;
40305     },
40306     
40307     getMinHeight: function(){
40308         return this.minHeight;
40309     }
40310 });
40311
40312
40313
40314
40315  
40316
40317
40318
40319
40320
40321
40322 Roo.bootstrap.layout.North = function(config)
40323 {
40324     config.region = 'north';
40325     config.cursor = 'n-resize';
40326     
40327     Roo.bootstrap.layout.Split.call(this, config);
40328     
40329     
40330     if(this.split){
40331         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40332         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40333         this.split.el.addClass("roo-layout-split-v");
40334     }
40335     //var size = config.initialSize || config.height;
40336     //if(this.el && typeof size != "undefined"){
40337     //    this.el.setHeight(size);
40338     //}
40339 };
40340 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40341 {
40342     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40343      
40344      
40345     onRender : function(ctr, pos)
40346     {
40347         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40348         var size = this.config.initialSize || this.config.height;
40349         if(this.el && typeof size != "undefined"){
40350             this.el.setHeight(size);
40351         }
40352     
40353     },
40354     
40355     getBox : function(){
40356         if(this.collapsed){
40357             return this.collapsedEl.getBox();
40358         }
40359         var box = this.el.getBox();
40360         if(this.split){
40361             box.height += this.split.el.getHeight();
40362         }
40363         return box;
40364     },
40365     
40366     updateBox : function(box){
40367         if(this.split && !this.collapsed){
40368             box.height -= this.split.el.getHeight();
40369             this.split.el.setLeft(box.x);
40370             this.split.el.setTop(box.y+box.height);
40371             this.split.el.setWidth(box.width);
40372         }
40373         if(this.collapsed){
40374             this.updateBody(box.width, null);
40375         }
40376         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40377     }
40378 });
40379
40380
40381
40382
40383
40384 Roo.bootstrap.layout.South = function(config){
40385     config.region = 'south';
40386     config.cursor = 's-resize';
40387     Roo.bootstrap.layout.Split.call(this, config);
40388     if(this.split){
40389         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40390         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40391         this.split.el.addClass("roo-layout-split-v");
40392     }
40393     
40394 };
40395
40396 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40397     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40398     
40399     onRender : function(ctr, pos)
40400     {
40401         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40402         var size = this.config.initialSize || this.config.height;
40403         if(this.el && typeof size != "undefined"){
40404             this.el.setHeight(size);
40405         }
40406     
40407     },
40408     
40409     getBox : function(){
40410         if(this.collapsed){
40411             return this.collapsedEl.getBox();
40412         }
40413         var box = this.el.getBox();
40414         if(this.split){
40415             var sh = this.split.el.getHeight();
40416             box.height += sh;
40417             box.y -= sh;
40418         }
40419         return box;
40420     },
40421     
40422     updateBox : function(box){
40423         if(this.split && !this.collapsed){
40424             var sh = this.split.el.getHeight();
40425             box.height -= sh;
40426             box.y += sh;
40427             this.split.el.setLeft(box.x);
40428             this.split.el.setTop(box.y-sh);
40429             this.split.el.setWidth(box.width);
40430         }
40431         if(this.collapsed){
40432             this.updateBody(box.width, null);
40433         }
40434         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40435     }
40436 });
40437
40438 Roo.bootstrap.layout.East = function(config){
40439     config.region = "east";
40440     config.cursor = "e-resize";
40441     Roo.bootstrap.layout.Split.call(this, config);
40442     if(this.split){
40443         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40444         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40445         this.split.el.addClass("roo-layout-split-h");
40446     }
40447     
40448 };
40449 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40450     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40451     
40452     onRender : function(ctr, pos)
40453     {
40454         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40455         var size = this.config.initialSize || this.config.width;
40456         if(this.el && typeof size != "undefined"){
40457             this.el.setWidth(size);
40458         }
40459     
40460     },
40461     
40462     getBox : function(){
40463         if(this.collapsed){
40464             return this.collapsedEl.getBox();
40465         }
40466         var box = this.el.getBox();
40467         if(this.split){
40468             var sw = this.split.el.getWidth();
40469             box.width += sw;
40470             box.x -= sw;
40471         }
40472         return box;
40473     },
40474
40475     updateBox : function(box){
40476         if(this.split && !this.collapsed){
40477             var sw = this.split.el.getWidth();
40478             box.width -= sw;
40479             this.split.el.setLeft(box.x);
40480             this.split.el.setTop(box.y);
40481             this.split.el.setHeight(box.height);
40482             box.x += sw;
40483         }
40484         if(this.collapsed){
40485             this.updateBody(null, box.height);
40486         }
40487         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40488     }
40489 });
40490
40491 Roo.bootstrap.layout.West = function(config){
40492     config.region = "west";
40493     config.cursor = "w-resize";
40494     
40495     Roo.bootstrap.layout.Split.call(this, config);
40496     if(this.split){
40497         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40498         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40499         this.split.el.addClass("roo-layout-split-h");
40500     }
40501     
40502 };
40503 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40504     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40505     
40506     onRender: function(ctr, pos)
40507     {
40508         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40509         var size = this.config.initialSize || this.config.width;
40510         if(typeof size != "undefined"){
40511             this.el.setWidth(size);
40512         }
40513     },
40514     
40515     getBox : function(){
40516         if(this.collapsed){
40517             return this.collapsedEl.getBox();
40518         }
40519         var box = this.el.getBox();
40520         if (box.width == 0) {
40521             box.width = this.config.width; // kludge?
40522         }
40523         if(this.split){
40524             box.width += this.split.el.getWidth();
40525         }
40526         return box;
40527     },
40528     
40529     updateBox : function(box){
40530         if(this.split && !this.collapsed){
40531             var sw = this.split.el.getWidth();
40532             box.width -= sw;
40533             this.split.el.setLeft(box.x+box.width);
40534             this.split.el.setTop(box.y);
40535             this.split.el.setHeight(box.height);
40536         }
40537         if(this.collapsed){
40538             this.updateBody(null, box.height);
40539         }
40540         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40541     }
40542 });Roo.namespace("Roo.bootstrap.panel");/*
40543  * Based on:
40544  * Ext JS Library 1.1.1
40545  * Copyright(c) 2006-2007, Ext JS, LLC.
40546  *
40547  * Originally Released Under LGPL - original licence link has changed is not relivant.
40548  *
40549  * Fork - LGPL
40550  * <script type="text/javascript">
40551  */
40552 /**
40553  * @class Roo.ContentPanel
40554  * @extends Roo.util.Observable
40555  * @builder-top
40556  * A basic ContentPanel element.
40557  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40558  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40559  * @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
40560  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40561  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40562  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40563  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40564  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40565  * @cfg {String} title          The title for this panel
40566  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40567  * @cfg {String} url            Calls {@link #setUrl} with this value
40568  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40569  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40570  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40571  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40572  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40573  * @cfg {Boolean} badges render the badges
40574  * @cfg {String} cls  extra classes to use  
40575  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40576
40577  * @constructor
40578  * Create a new ContentPanel.
40579  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40580  * @param {String/Object} config A string to set only the title or a config object
40581  * @param {String} content (optional) Set the HTML content for this panel
40582  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40583  */
40584 Roo.bootstrap.panel.Content = function( config){
40585     
40586     this.tpl = config.tpl || false;
40587     
40588     var el = config.el;
40589     var content = config.content;
40590
40591     if(config.autoCreate){ // xtype is available if this is called from factory
40592         el = Roo.id();
40593     }
40594     this.el = Roo.get(el);
40595     if(!this.el && config && config.autoCreate){
40596         if(typeof config.autoCreate == "object"){
40597             if(!config.autoCreate.id){
40598                 config.autoCreate.id = config.id||el;
40599             }
40600             this.el = Roo.DomHelper.append(document.body,
40601                         config.autoCreate, true);
40602         }else{
40603             var elcfg =  {
40604                 tag: "div",
40605                 cls: (config.cls || '') +
40606                     (config.background ? ' bg-' + config.background : '') +
40607                     " roo-layout-inactive-content",
40608                 id: config.id||el
40609             };
40610             if (config.iframe) {
40611                 elcfg.cn = [
40612                     {
40613                         tag : 'iframe',
40614                         style : 'border: 0px',
40615                         src : 'about:blank'
40616                     }
40617                 ];
40618             }
40619               
40620             if (config.html) {
40621                 elcfg.html = config.html;
40622                 
40623             }
40624                         
40625             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40626             if (config.iframe) {
40627                 this.iframeEl = this.el.select('iframe',true).first();
40628             }
40629             
40630         }
40631     } 
40632     this.closable = false;
40633     this.loaded = false;
40634     this.active = false;
40635    
40636       
40637     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40638         
40639         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40640         
40641         this.wrapEl = this.el; //this.el.wrap();
40642         var ti = [];
40643         if (config.toolbar.items) {
40644             ti = config.toolbar.items ;
40645             delete config.toolbar.items ;
40646         }
40647         
40648         var nitems = [];
40649         this.toolbar.render(this.wrapEl, 'before');
40650         for(var i =0;i < ti.length;i++) {
40651           //  Roo.log(['add child', items[i]]);
40652             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40653         }
40654         this.toolbar.items = nitems;
40655         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40656         delete config.toolbar;
40657         
40658     }
40659     /*
40660     // xtype created footer. - not sure if will work as we normally have to render first..
40661     if (this.footer && !this.footer.el && this.footer.xtype) {
40662         if (!this.wrapEl) {
40663             this.wrapEl = this.el.wrap();
40664         }
40665     
40666         this.footer.container = this.wrapEl.createChild();
40667          
40668         this.footer = Roo.factory(this.footer, Roo);
40669         
40670     }
40671     */
40672     
40673      if(typeof config == "string"){
40674         this.title = config;
40675     }else{
40676         Roo.apply(this, config);
40677     }
40678     
40679     if(this.resizeEl){
40680         this.resizeEl = Roo.get(this.resizeEl, true);
40681     }else{
40682         this.resizeEl = this.el;
40683     }
40684     // handle view.xtype
40685     
40686  
40687     
40688     
40689     this.addEvents({
40690         /**
40691          * @event activate
40692          * Fires when this panel is activated. 
40693          * @param {Roo.ContentPanel} this
40694          */
40695         "activate" : true,
40696         /**
40697          * @event deactivate
40698          * Fires when this panel is activated. 
40699          * @param {Roo.ContentPanel} this
40700          */
40701         "deactivate" : true,
40702
40703         /**
40704          * @event resize
40705          * Fires when this panel is resized if fitToFrame is true.
40706          * @param {Roo.ContentPanel} this
40707          * @param {Number} width The width after any component adjustments
40708          * @param {Number} height The height after any component adjustments
40709          */
40710         "resize" : true,
40711         
40712          /**
40713          * @event render
40714          * Fires when this tab is created
40715          * @param {Roo.ContentPanel} this
40716          */
40717         "render" : true,
40718         
40719           /**
40720          * @event scroll
40721          * Fires when this content is scrolled
40722          * @param {Roo.ContentPanel} this
40723          * @param {Event} scrollEvent
40724          */
40725         "scroll" : true
40726         
40727         
40728         
40729     });
40730     
40731
40732     
40733     
40734     if(this.autoScroll && !this.iframe){
40735         this.resizeEl.setStyle("overflow", "auto");
40736         this.resizeEl.on('scroll', this.onScroll, this);
40737     } else {
40738         // fix randome scrolling
40739         //this.el.on('scroll', function() {
40740         //    Roo.log('fix random scolling');
40741         //    this.scrollTo('top',0); 
40742         //});
40743     }
40744     content = content || this.content;
40745     if(content){
40746         this.setContent(content);
40747     }
40748     if(config && config.url){
40749         this.setUrl(this.url, this.params, this.loadOnce);
40750     }
40751     
40752     
40753     
40754     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40755     
40756     if (this.view && typeof(this.view.xtype) != 'undefined') {
40757         this.view.el = this.el.appendChild(document.createElement("div"));
40758         this.view = Roo.factory(this.view); 
40759         this.view.render  &&  this.view.render(false, '');  
40760     }
40761     
40762     
40763     this.fireEvent('render', this);
40764 };
40765
40766 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40767     
40768     cls : '',
40769     background : '',
40770     
40771     tabTip : '',
40772     
40773     iframe : false,
40774     iframeEl : false,
40775     
40776     /* Resize Element - use this to work out scroll etc. */
40777     resizeEl : false,
40778     
40779     setRegion : function(region){
40780         this.region = region;
40781         this.setActiveClass(region && !this.background);
40782     },
40783     
40784     
40785     setActiveClass: function(state)
40786     {
40787         if(state){
40788            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40789            this.el.setStyle('position','relative');
40790         }else{
40791            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40792            this.el.setStyle('position', 'absolute');
40793         } 
40794     },
40795     
40796     /**
40797      * Returns the toolbar for this Panel if one was configured. 
40798      * @return {Roo.Toolbar} 
40799      */
40800     getToolbar : function(){
40801         return this.toolbar;
40802     },
40803     
40804     setActiveState : function(active)
40805     {
40806         this.active = active;
40807         this.setActiveClass(active);
40808         if(!active){
40809             if(this.fireEvent("deactivate", this) === false){
40810                 return false;
40811             }
40812             return true;
40813         }
40814         this.fireEvent("activate", this);
40815         return true;
40816     },
40817     /**
40818      * Updates this panel's element (not for iframe)
40819      * @param {String} content The new content
40820      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40821     */
40822     setContent : function(content, loadScripts){
40823         if (this.iframe) {
40824             return;
40825         }
40826         
40827         this.el.update(content, loadScripts);
40828     },
40829
40830     ignoreResize : function(w, h){
40831         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40832             return true;
40833         }else{
40834             this.lastSize = {width: w, height: h};
40835             return false;
40836         }
40837     },
40838     /**
40839      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40840      * @return {Roo.UpdateManager} The UpdateManager
40841      */
40842     getUpdateManager : function(){
40843         if (this.iframe) {
40844             return false;
40845         }
40846         return this.el.getUpdateManager();
40847     },
40848      /**
40849      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40850      * Does not work with IFRAME contents
40851      * @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:
40852 <pre><code>
40853 panel.load({
40854     url: "your-url.php",
40855     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40856     callback: yourFunction,
40857     scope: yourObject, //(optional scope)
40858     discardUrl: false,
40859     nocache: false,
40860     text: "Loading...",
40861     timeout: 30,
40862     scripts: false
40863 });
40864 </code></pre>
40865      
40866      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40867      * 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.
40868      * @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}
40869      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40870      * @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.
40871      * @return {Roo.ContentPanel} this
40872      */
40873     load : function(){
40874         
40875         if (this.iframe) {
40876             return this;
40877         }
40878         
40879         var um = this.el.getUpdateManager();
40880         um.update.apply(um, arguments);
40881         return this;
40882     },
40883
40884
40885     /**
40886      * 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.
40887      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40888      * @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)
40889      * @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)
40890      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40891      */
40892     setUrl : function(url, params, loadOnce){
40893         if (this.iframe) {
40894             this.iframeEl.dom.src = url;
40895             return false;
40896         }
40897         
40898         if(this.refreshDelegate){
40899             this.removeListener("activate", this.refreshDelegate);
40900         }
40901         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40902         this.on("activate", this.refreshDelegate);
40903         return this.el.getUpdateManager();
40904     },
40905     
40906     _handleRefresh : function(url, params, loadOnce){
40907         if(!loadOnce || !this.loaded){
40908             var updater = this.el.getUpdateManager();
40909             updater.update(url, params, this._setLoaded.createDelegate(this));
40910         }
40911     },
40912     
40913     _setLoaded : function(){
40914         this.loaded = true;
40915     }, 
40916     
40917     /**
40918      * Returns this panel's id
40919      * @return {String} 
40920      */
40921     getId : function(){
40922         return this.el.id;
40923     },
40924     
40925     /** 
40926      * Returns this panel's element - used by regiosn to add.
40927      * @return {Roo.Element} 
40928      */
40929     getEl : function(){
40930         return this.wrapEl || this.el;
40931     },
40932     
40933    
40934     
40935     adjustForComponents : function(width, height)
40936     {
40937         //Roo.log('adjustForComponents ');
40938         if(this.resizeEl != this.el){
40939             width -= this.el.getFrameWidth('lr');
40940             height -= this.el.getFrameWidth('tb');
40941         }
40942         if(this.toolbar){
40943             var te = this.toolbar.getEl();
40944             te.setWidth(width);
40945             height -= te.getHeight();
40946         }
40947         if(this.footer){
40948             var te = this.footer.getEl();
40949             te.setWidth(width);
40950             height -= te.getHeight();
40951         }
40952         
40953         
40954         if(this.adjustments){
40955             width += this.adjustments[0];
40956             height += this.adjustments[1];
40957         }
40958         return {"width": width, "height": height};
40959     },
40960     
40961     setSize : function(width, height){
40962         if(this.fitToFrame && !this.ignoreResize(width, height)){
40963             if(this.fitContainer && this.resizeEl != this.el){
40964                 this.el.setSize(width, height);
40965             }
40966             var size = this.adjustForComponents(width, height);
40967             if (this.iframe) {
40968                 this.iframeEl.setSize(width,height);
40969             }
40970             
40971             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40972             this.fireEvent('resize', this, size.width, size.height);
40973             
40974             
40975         }
40976     },
40977     
40978     /**
40979      * Returns this panel's title
40980      * @return {String} 
40981      */
40982     getTitle : function(){
40983         
40984         if (typeof(this.title) != 'object') {
40985             return this.title;
40986         }
40987         
40988         var t = '';
40989         for (var k in this.title) {
40990             if (!this.title.hasOwnProperty(k)) {
40991                 continue;
40992             }
40993             
40994             if (k.indexOf('-') >= 0) {
40995                 var s = k.split('-');
40996                 for (var i = 0; i<s.length; i++) {
40997                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40998                 }
40999             } else {
41000                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41001             }
41002         }
41003         return t;
41004     },
41005     
41006     /**
41007      * Set this panel's title
41008      * @param {String} title
41009      */
41010     setTitle : function(title){
41011         this.title = title;
41012         if(this.region){
41013             this.region.updatePanelTitle(this, title);
41014         }
41015     },
41016     
41017     /**
41018      * Returns true is this panel was configured to be closable
41019      * @return {Boolean} 
41020      */
41021     isClosable : function(){
41022         return this.closable;
41023     },
41024     
41025     beforeSlide : function(){
41026         this.el.clip();
41027         this.resizeEl.clip();
41028     },
41029     
41030     afterSlide : function(){
41031         this.el.unclip();
41032         this.resizeEl.unclip();
41033     },
41034     
41035     /**
41036      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41037      *   Will fail silently if the {@link #setUrl} method has not been called.
41038      *   This does not activate the panel, just updates its content.
41039      */
41040     refresh : function(){
41041         if(this.refreshDelegate){
41042            this.loaded = false;
41043            this.refreshDelegate();
41044         }
41045     },
41046     
41047     /**
41048      * Destroys this panel
41049      */
41050     destroy : function(){
41051         this.el.removeAllListeners();
41052         var tempEl = document.createElement("span");
41053         tempEl.appendChild(this.el.dom);
41054         tempEl.innerHTML = "";
41055         this.el.remove();
41056         this.el = null;
41057     },
41058     
41059     /**
41060      * form - if the content panel contains a form - this is a reference to it.
41061      * @type {Roo.form.Form}
41062      */
41063     form : false,
41064     /**
41065      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41066      *    This contains a reference to it.
41067      * @type {Roo.View}
41068      */
41069     view : false,
41070     
41071       /**
41072      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41073      * <pre><code>
41074
41075 layout.addxtype({
41076        xtype : 'Form',
41077        items: [ .... ]
41078    }
41079 );
41080
41081 </code></pre>
41082      * @param {Object} cfg Xtype definition of item to add.
41083      */
41084     
41085     
41086     getChildContainer: function () {
41087         return this.getEl();
41088     },
41089     
41090     
41091     onScroll : function(e)
41092     {
41093         this.fireEvent('scroll', this, e);
41094     }
41095     
41096     
41097     /*
41098         var  ret = new Roo.factory(cfg);
41099         return ret;
41100         
41101         
41102         // add form..
41103         if (cfg.xtype.match(/^Form$/)) {
41104             
41105             var el;
41106             //if (this.footer) {
41107             //    el = this.footer.container.insertSibling(false, 'before');
41108             //} else {
41109                 el = this.el.createChild();
41110             //}
41111
41112             this.form = new  Roo.form.Form(cfg);
41113             
41114             
41115             if ( this.form.allItems.length) {
41116                 this.form.render(el.dom);
41117             }
41118             return this.form;
41119         }
41120         // should only have one of theses..
41121         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41122             // views.. should not be just added - used named prop 'view''
41123             
41124             cfg.el = this.el.appendChild(document.createElement("div"));
41125             // factory?
41126             
41127             var ret = new Roo.factory(cfg);
41128              
41129              ret.render && ret.render(false, ''); // render blank..
41130             this.view = ret;
41131             return ret;
41132         }
41133         return false;
41134     }
41135     \*/
41136 });
41137  
41138 /**
41139  * @class Roo.bootstrap.panel.Grid
41140  * @extends Roo.bootstrap.panel.Content
41141  * @constructor
41142  * Create a new GridPanel.
41143  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41144  * @param {Object} config A the config object
41145   
41146  */
41147
41148
41149
41150 Roo.bootstrap.panel.Grid = function(config)
41151 {
41152     
41153       
41154     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41155         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41156
41157     config.el = this.wrapper;
41158     //this.el = this.wrapper;
41159     
41160       if (config.container) {
41161         // ctor'ed from a Border/panel.grid
41162         
41163         
41164         this.wrapper.setStyle("overflow", "hidden");
41165         this.wrapper.addClass('roo-grid-container');
41166
41167     }
41168     
41169     
41170     if(config.toolbar){
41171         var tool_el = this.wrapper.createChild();    
41172         this.toolbar = Roo.factory(config.toolbar);
41173         var ti = [];
41174         if (config.toolbar.items) {
41175             ti = config.toolbar.items ;
41176             delete config.toolbar.items ;
41177         }
41178         
41179         var nitems = [];
41180         this.toolbar.render(tool_el);
41181         for(var i =0;i < ti.length;i++) {
41182           //  Roo.log(['add child', items[i]]);
41183             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41184         }
41185         this.toolbar.items = nitems;
41186         
41187         delete config.toolbar;
41188     }
41189     
41190     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41191     config.grid.scrollBody = true;;
41192     config.grid.monitorWindowResize = false; // turn off autosizing
41193     config.grid.autoHeight = false;
41194     config.grid.autoWidth = false;
41195     
41196     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41197     
41198     if (config.background) {
41199         // render grid on panel activation (if panel background)
41200         this.on('activate', function(gp) {
41201             if (!gp.grid.rendered) {
41202                 gp.grid.render(this.wrapper);
41203                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41204             }
41205         });
41206             
41207     } else {
41208         this.grid.render(this.wrapper);
41209         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41210
41211     }
41212     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41213     // ??? needed ??? config.el = this.wrapper;
41214     
41215     
41216     
41217   
41218     // xtype created footer. - not sure if will work as we normally have to render first..
41219     if (this.footer && !this.footer.el && this.footer.xtype) {
41220         
41221         var ctr = this.grid.getView().getFooterPanel(true);
41222         this.footer.dataSource = this.grid.dataSource;
41223         this.footer = Roo.factory(this.footer, Roo);
41224         this.footer.render(ctr);
41225         
41226     }
41227     
41228     
41229     
41230     
41231      
41232 };
41233
41234 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41235     getId : function(){
41236         return this.grid.id;
41237     },
41238     
41239     /**
41240      * Returns the grid for this panel
41241      * @return {Roo.bootstrap.Table} 
41242      */
41243     getGrid : function(){
41244         return this.grid;    
41245     },
41246     
41247     setSize : function(width, height){
41248         if(!this.ignoreResize(width, height)){
41249             var grid = this.grid;
41250             var size = this.adjustForComponents(width, height);
41251             // tfoot is not a footer?
41252           
41253             
41254             var gridel = grid.getGridEl();
41255             gridel.setSize(size.width, size.height);
41256             
41257             var tbd = grid.getGridEl().select('tbody', true).first();
41258             var thd = grid.getGridEl().select('thead',true).first();
41259             var tbf= grid.getGridEl().select('tfoot', true).first();
41260
41261             if (tbf) {
41262                 size.height -= tbf.getHeight();
41263             }
41264             if (thd) {
41265                 size.height -= thd.getHeight();
41266             }
41267             
41268             tbd.setSize(size.width, size.height );
41269             // this is for the account management tab -seems to work there.
41270             var thd = grid.getGridEl().select('thead',true).first();
41271             //if (tbd) {
41272             //    tbd.setSize(size.width, size.height - thd.getHeight());
41273             //}
41274              
41275             grid.autoSize();
41276         }
41277     },
41278      
41279     
41280     
41281     beforeSlide : function(){
41282         this.grid.getView().scroller.clip();
41283     },
41284     
41285     afterSlide : function(){
41286         this.grid.getView().scroller.unclip();
41287     },
41288     
41289     destroy : function(){
41290         this.grid.destroy();
41291         delete this.grid;
41292         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41293     }
41294 });
41295
41296 /**
41297  * @class Roo.bootstrap.panel.Nest
41298  * @extends Roo.bootstrap.panel.Content
41299  * @constructor
41300  * Create a new Panel, that can contain a layout.Border.
41301  * 
41302  * 
41303  * @param {Roo.BorderLayout} layout The layout for this panel
41304  * @param {String/Object} config A string to set only the title or a config object
41305  */
41306 Roo.bootstrap.panel.Nest = function(config)
41307 {
41308     // construct with only one argument..
41309     /* FIXME - implement nicer consturctors
41310     if (layout.layout) {
41311         config = layout;
41312         layout = config.layout;
41313         delete config.layout;
41314     }
41315     if (layout.xtype && !layout.getEl) {
41316         // then layout needs constructing..
41317         layout = Roo.factory(layout, Roo);
41318     }
41319     */
41320     
41321     config.el =  config.layout.getEl();
41322     
41323     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41324     
41325     config.layout.monitorWindowResize = false; // turn off autosizing
41326     this.layout = config.layout;
41327     this.layout.getEl().addClass("roo-layout-nested-layout");
41328     this.layout.parent = this;
41329     
41330     
41331     
41332     
41333 };
41334
41335 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41336
41337     setSize : function(width, height){
41338         if(!this.ignoreResize(width, height)){
41339             var size = this.adjustForComponents(width, height);
41340             var el = this.layout.getEl();
41341             if (size.height < 1) {
41342                 el.setWidth(size.width);   
41343             } else {
41344                 el.setSize(size.width, size.height);
41345             }
41346             var touch = el.dom.offsetWidth;
41347             this.layout.layout();
41348             // ie requires a double layout on the first pass
41349             if(Roo.isIE && !this.initialized){
41350                 this.initialized = true;
41351                 this.layout.layout();
41352             }
41353         }
41354     },
41355     
41356     // activate all subpanels if not currently active..
41357     
41358     setActiveState : function(active){
41359         this.active = active;
41360         this.setActiveClass(active);
41361         
41362         if(!active){
41363             this.fireEvent("deactivate", this);
41364             return;
41365         }
41366         
41367         this.fireEvent("activate", this);
41368         // not sure if this should happen before or after..
41369         if (!this.layout) {
41370             return; // should not happen..
41371         }
41372         var reg = false;
41373         for (var r in this.layout.regions) {
41374             reg = this.layout.getRegion(r);
41375             if (reg.getActivePanel()) {
41376                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41377                 reg.setActivePanel(reg.getActivePanel());
41378                 continue;
41379             }
41380             if (!reg.panels.length) {
41381                 continue;
41382             }
41383             reg.showPanel(reg.getPanel(0));
41384         }
41385         
41386         
41387         
41388         
41389     },
41390     
41391     /**
41392      * Returns the nested BorderLayout for this panel
41393      * @return {Roo.BorderLayout} 
41394      */
41395     getLayout : function(){
41396         return this.layout;
41397     },
41398     
41399      /**
41400      * Adds a xtype elements to the layout of the nested panel
41401      * <pre><code>
41402
41403 panel.addxtype({
41404        xtype : 'ContentPanel',
41405        region: 'west',
41406        items: [ .... ]
41407    }
41408 );
41409
41410 panel.addxtype({
41411         xtype : 'NestedLayoutPanel',
41412         region: 'west',
41413         layout: {
41414            center: { },
41415            west: { }   
41416         },
41417         items : [ ... list of content panels or nested layout panels.. ]
41418    }
41419 );
41420 </code></pre>
41421      * @param {Object} cfg Xtype definition of item to add.
41422      */
41423     addxtype : function(cfg) {
41424         return this.layout.addxtype(cfg);
41425     
41426     }
41427 });/*
41428  * Based on:
41429  * Ext JS Library 1.1.1
41430  * Copyright(c) 2006-2007, Ext JS, LLC.
41431  *
41432  * Originally Released Under LGPL - original licence link has changed is not relivant.
41433  *
41434  * Fork - LGPL
41435  * <script type="text/javascript">
41436  */
41437 /**
41438  * @class Roo.TabPanel
41439  * @extends Roo.util.Observable
41440  * A lightweight tab container.
41441  * <br><br>
41442  * Usage:
41443  * <pre><code>
41444 // basic tabs 1, built from existing content
41445 var tabs = new Roo.TabPanel("tabs1");
41446 tabs.addTab("script", "View Script");
41447 tabs.addTab("markup", "View Markup");
41448 tabs.activate("script");
41449
41450 // more advanced tabs, built from javascript
41451 var jtabs = new Roo.TabPanel("jtabs");
41452 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41453
41454 // set up the UpdateManager
41455 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41456 var updater = tab2.getUpdateManager();
41457 updater.setDefaultUrl("ajax1.htm");
41458 tab2.on('activate', updater.refresh, updater, true);
41459
41460 // Use setUrl for Ajax loading
41461 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41462 tab3.setUrl("ajax2.htm", null, true);
41463
41464 // Disabled tab
41465 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41466 tab4.disable();
41467
41468 jtabs.activate("jtabs-1");
41469  * </code></pre>
41470  * @constructor
41471  * Create a new TabPanel.
41472  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41473  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41474  */
41475 Roo.bootstrap.panel.Tabs = function(config){
41476     /**
41477     * The container element for this TabPanel.
41478     * @type Roo.Element
41479     */
41480     this.el = Roo.get(config.el);
41481     delete config.el;
41482     if(config){
41483         if(typeof config == "boolean"){
41484             this.tabPosition = config ? "bottom" : "top";
41485         }else{
41486             Roo.apply(this, config);
41487         }
41488     }
41489     
41490     if(this.tabPosition == "bottom"){
41491         // if tabs are at the bottom = create the body first.
41492         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41493         this.el.addClass("roo-tabs-bottom");
41494     }
41495     // next create the tabs holders
41496     
41497     if (this.tabPosition == "west"){
41498         
41499         var reg = this.region; // fake it..
41500         while (reg) {
41501             if (!reg.mgr.parent) {
41502                 break;
41503             }
41504             reg = reg.mgr.parent.region;
41505         }
41506         Roo.log("got nest?");
41507         Roo.log(reg);
41508         if (reg.mgr.getRegion('west')) {
41509             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41510             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41511             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41512             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41513             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41514         
41515             
41516         }
41517         
41518         
41519     } else {
41520      
41521         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41522         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41523         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41524         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41525     }
41526     
41527     
41528     if(Roo.isIE){
41529         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41530     }
41531     
41532     // finally - if tabs are at the top, then create the body last..
41533     if(this.tabPosition != "bottom"){
41534         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41535          * @type Roo.Element
41536          */
41537         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41538         this.el.addClass("roo-tabs-top");
41539     }
41540     this.items = [];
41541
41542     this.bodyEl.setStyle("position", "relative");
41543
41544     this.active = null;
41545     this.activateDelegate = this.activate.createDelegate(this);
41546
41547     this.addEvents({
41548         /**
41549          * @event tabchange
41550          * Fires when the active tab changes
41551          * @param {Roo.TabPanel} this
41552          * @param {Roo.TabPanelItem} activePanel The new active tab
41553          */
41554         "tabchange": true,
41555         /**
41556          * @event beforetabchange
41557          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41558          * @param {Roo.TabPanel} this
41559          * @param {Object} e Set cancel to true on this object to cancel the tab change
41560          * @param {Roo.TabPanelItem} tab The tab being changed to
41561          */
41562         "beforetabchange" : true
41563     });
41564
41565     Roo.EventManager.onWindowResize(this.onResize, this);
41566     this.cpad = this.el.getPadding("lr");
41567     this.hiddenCount = 0;
41568
41569
41570     // toolbar on the tabbar support...
41571     if (this.toolbar) {
41572         alert("no toolbar support yet");
41573         this.toolbar  = false;
41574         /*
41575         var tcfg = this.toolbar;
41576         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41577         this.toolbar = new Roo.Toolbar(tcfg);
41578         if (Roo.isSafari) {
41579             var tbl = tcfg.container.child('table', true);
41580             tbl.setAttribute('width', '100%');
41581         }
41582         */
41583         
41584     }
41585    
41586
41587
41588     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41589 };
41590
41591 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41592     /*
41593      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41594      */
41595     tabPosition : "top",
41596     /*
41597      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41598      */
41599     currentTabWidth : 0,
41600     /*
41601      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41602      */
41603     minTabWidth : 40,
41604     /*
41605      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41606      */
41607     maxTabWidth : 250,
41608     /*
41609      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41610      */
41611     preferredTabWidth : 175,
41612     /*
41613      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41614      */
41615     resizeTabs : false,
41616     /*
41617      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41618      */
41619     monitorResize : true,
41620     /*
41621      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41622      */
41623     toolbar : false,  // set by caller..
41624     
41625     region : false, /// set by caller
41626     
41627     disableTooltips : true, // not used yet...
41628
41629     /**
41630      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41631      * @param {String} id The id of the div to use <b>or create</b>
41632      * @param {String} text The text for the tab
41633      * @param {String} content (optional) Content to put in the TabPanelItem body
41634      * @param {Boolean} closable (optional) True to create a close icon on the tab
41635      * @return {Roo.TabPanelItem} The created TabPanelItem
41636      */
41637     addTab : function(id, text, content, closable, tpl)
41638     {
41639         var item = new Roo.bootstrap.panel.TabItem({
41640             panel: this,
41641             id : id,
41642             text : text,
41643             closable : closable,
41644             tpl : tpl
41645         });
41646         this.addTabItem(item);
41647         if(content){
41648             item.setContent(content);
41649         }
41650         return item;
41651     },
41652
41653     /**
41654      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41655      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41656      * @return {Roo.TabPanelItem}
41657      */
41658     getTab : function(id){
41659         return this.items[id];
41660     },
41661
41662     /**
41663      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41664      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41665      */
41666     hideTab : function(id){
41667         var t = this.items[id];
41668         if(!t.isHidden()){
41669            t.setHidden(true);
41670            this.hiddenCount++;
41671            this.autoSizeTabs();
41672         }
41673     },
41674
41675     /**
41676      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41677      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41678      */
41679     unhideTab : function(id){
41680         var t = this.items[id];
41681         if(t.isHidden()){
41682            t.setHidden(false);
41683            this.hiddenCount--;
41684            this.autoSizeTabs();
41685         }
41686     },
41687
41688     /**
41689      * Adds an existing {@link Roo.TabPanelItem}.
41690      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41691      */
41692     addTabItem : function(item)
41693     {
41694         this.items[item.id] = item;
41695         this.items.push(item);
41696         this.autoSizeTabs();
41697       //  if(this.resizeTabs){
41698     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41699   //         this.autoSizeTabs();
41700 //        }else{
41701 //            item.autoSize();
41702        // }
41703     },
41704
41705     /**
41706      * Removes a {@link Roo.TabPanelItem}.
41707      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41708      */
41709     removeTab : function(id){
41710         var items = this.items;
41711         var tab = items[id];
41712         if(!tab) { return; }
41713         var index = items.indexOf(tab);
41714         if(this.active == tab && items.length > 1){
41715             var newTab = this.getNextAvailable(index);
41716             if(newTab) {
41717                 newTab.activate();
41718             }
41719         }
41720         this.stripEl.dom.removeChild(tab.pnode.dom);
41721         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41722             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41723         }
41724         items.splice(index, 1);
41725         delete this.items[tab.id];
41726         tab.fireEvent("close", tab);
41727         tab.purgeListeners();
41728         this.autoSizeTabs();
41729     },
41730
41731     getNextAvailable : function(start){
41732         var items = this.items;
41733         var index = start;
41734         // look for a next tab that will slide over to
41735         // replace the one being removed
41736         while(index < items.length){
41737             var item = items[++index];
41738             if(item && !item.isHidden()){
41739                 return item;
41740             }
41741         }
41742         // if one isn't found select the previous tab (on the left)
41743         index = start;
41744         while(index >= 0){
41745             var item = items[--index];
41746             if(item && !item.isHidden()){
41747                 return item;
41748             }
41749         }
41750         return null;
41751     },
41752
41753     /**
41754      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41755      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41756      */
41757     disableTab : function(id){
41758         var tab = this.items[id];
41759         if(tab && this.active != tab){
41760             tab.disable();
41761         }
41762     },
41763
41764     /**
41765      * Enables a {@link Roo.TabPanelItem} that is disabled.
41766      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41767      */
41768     enableTab : function(id){
41769         var tab = this.items[id];
41770         tab.enable();
41771     },
41772
41773     /**
41774      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41775      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41776      * @return {Roo.TabPanelItem} The TabPanelItem.
41777      */
41778     activate : function(id)
41779     {
41780         //Roo.log('activite:'  + id);
41781         
41782         var tab = this.items[id];
41783         if(!tab){
41784             return null;
41785         }
41786         if(tab == this.active || tab.disabled){
41787             return tab;
41788         }
41789         var e = {};
41790         this.fireEvent("beforetabchange", this, e, tab);
41791         if(e.cancel !== true && !tab.disabled){
41792             if(this.active){
41793                 this.active.hide();
41794             }
41795             this.active = this.items[id];
41796             this.active.show();
41797             this.fireEvent("tabchange", this, this.active);
41798         }
41799         return tab;
41800     },
41801
41802     /**
41803      * Gets the active {@link Roo.TabPanelItem}.
41804      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41805      */
41806     getActiveTab : function(){
41807         return this.active;
41808     },
41809
41810     /**
41811      * Updates the tab body element to fit the height of the container element
41812      * for overflow scrolling
41813      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41814      */
41815     syncHeight : function(targetHeight){
41816         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41817         var bm = this.bodyEl.getMargins();
41818         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41819         this.bodyEl.setHeight(newHeight);
41820         return newHeight;
41821     },
41822
41823     onResize : function(){
41824         if(this.monitorResize){
41825             this.autoSizeTabs();
41826         }
41827     },
41828
41829     /**
41830      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41831      */
41832     beginUpdate : function(){
41833         this.updating = true;
41834     },
41835
41836     /**
41837      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41838      */
41839     endUpdate : function(){
41840         this.updating = false;
41841         this.autoSizeTabs();
41842     },
41843
41844     /**
41845      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41846      */
41847     autoSizeTabs : function()
41848     {
41849         var count = this.items.length;
41850         var vcount = count - this.hiddenCount;
41851         
41852         if (vcount < 2) {
41853             this.stripEl.hide();
41854         } else {
41855             this.stripEl.show();
41856         }
41857         
41858         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41859             return;
41860         }
41861         
41862         
41863         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41864         var availWidth = Math.floor(w / vcount);
41865         var b = this.stripBody;
41866         if(b.getWidth() > w){
41867             var tabs = this.items;
41868             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41869             if(availWidth < this.minTabWidth){
41870                 /*if(!this.sleft){    // incomplete scrolling code
41871                     this.createScrollButtons();
41872                 }
41873                 this.showScroll();
41874                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41875             }
41876         }else{
41877             if(this.currentTabWidth < this.preferredTabWidth){
41878                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41879             }
41880         }
41881     },
41882
41883     /**
41884      * Returns the number of tabs in this TabPanel.
41885      * @return {Number}
41886      */
41887      getCount : function(){
41888          return this.items.length;
41889      },
41890
41891     /**
41892      * Resizes all the tabs to the passed width
41893      * @param {Number} The new width
41894      */
41895     setTabWidth : function(width){
41896         this.currentTabWidth = width;
41897         for(var i = 0, len = this.items.length; i < len; i++) {
41898                 if(!this.items[i].isHidden()) {
41899                 this.items[i].setWidth(width);
41900             }
41901         }
41902     },
41903
41904     /**
41905      * Destroys this TabPanel
41906      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41907      */
41908     destroy : function(removeEl){
41909         Roo.EventManager.removeResizeListener(this.onResize, this);
41910         for(var i = 0, len = this.items.length; i < len; i++){
41911             this.items[i].purgeListeners();
41912         }
41913         if(removeEl === true){
41914             this.el.update("");
41915             this.el.remove();
41916         }
41917     },
41918     
41919     createStrip : function(container)
41920     {
41921         var strip = document.createElement("nav");
41922         strip.className = Roo.bootstrap.version == 4 ?
41923             "navbar-light bg-light" : 
41924             "navbar navbar-default"; //"x-tabs-wrap";
41925         container.appendChild(strip);
41926         return strip;
41927     },
41928     
41929     createStripList : function(strip)
41930     {
41931         // div wrapper for retard IE
41932         // returns the "tr" element.
41933         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41934         //'<div class="x-tabs-strip-wrap">'+
41935           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41936           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41937         return strip.firstChild; //.firstChild.firstChild.firstChild;
41938     },
41939     createBody : function(container)
41940     {
41941         var body = document.createElement("div");
41942         Roo.id(body, "tab-body");
41943         //Roo.fly(body).addClass("x-tabs-body");
41944         Roo.fly(body).addClass("tab-content");
41945         container.appendChild(body);
41946         return body;
41947     },
41948     createItemBody :function(bodyEl, id){
41949         var body = Roo.getDom(id);
41950         if(!body){
41951             body = document.createElement("div");
41952             body.id = id;
41953         }
41954         //Roo.fly(body).addClass("x-tabs-item-body");
41955         Roo.fly(body).addClass("tab-pane");
41956          bodyEl.insertBefore(body, bodyEl.firstChild);
41957         return body;
41958     },
41959     /** @private */
41960     createStripElements :  function(stripEl, text, closable, tpl)
41961     {
41962         var td = document.createElement("li"); // was td..
41963         td.className = 'nav-item';
41964         
41965         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41966         
41967         
41968         stripEl.appendChild(td);
41969         /*if(closable){
41970             td.className = "x-tabs-closable";
41971             if(!this.closeTpl){
41972                 this.closeTpl = new Roo.Template(
41973                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41974                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41975                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41976                 );
41977             }
41978             var el = this.closeTpl.overwrite(td, {"text": text});
41979             var close = el.getElementsByTagName("div")[0];
41980             var inner = el.getElementsByTagName("em")[0];
41981             return {"el": el, "close": close, "inner": inner};
41982         } else {
41983         */
41984         // not sure what this is..
41985 //            if(!this.tabTpl){
41986                 //this.tabTpl = new Roo.Template(
41987                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41988                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41989                 //);
41990 //                this.tabTpl = new Roo.Template(
41991 //                   '<a href="#">' +
41992 //                   '<span unselectable="on"' +
41993 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41994 //                            ' >{text}</span></a>'
41995 //                );
41996 //                
41997 //            }
41998
41999
42000             var template = tpl || this.tabTpl || false;
42001             
42002             if(!template){
42003                 template =  new Roo.Template(
42004                         Roo.bootstrap.version == 4 ? 
42005                             (
42006                                 '<a class="nav-link" href="#" unselectable="on"' +
42007                                      (this.disableTooltips ? '' : ' title="{text}"') +
42008                                      ' >{text}</a>'
42009                             ) : (
42010                                 '<a class="nav-link" href="#">' +
42011                                 '<span unselectable="on"' +
42012                                          (this.disableTooltips ? '' : ' title="{text}"') +
42013                                     ' >{text}</span></a>'
42014                             )
42015                 );
42016             }
42017             
42018             switch (typeof(template)) {
42019                 case 'object' :
42020                     break;
42021                 case 'string' :
42022                     template = new Roo.Template(template);
42023                     break;
42024                 default :
42025                     break;
42026             }
42027             
42028             var el = template.overwrite(td, {"text": text});
42029             
42030             var inner = el.getElementsByTagName("span")[0];
42031             
42032             return {"el": el, "inner": inner};
42033             
42034     }
42035         
42036     
42037 });
42038
42039 /**
42040  * @class Roo.TabPanelItem
42041  * @extends Roo.util.Observable
42042  * Represents an individual item (tab plus body) in a TabPanel.
42043  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42044  * @param {String} id The id of this TabPanelItem
42045  * @param {String} text The text for the tab of this TabPanelItem
42046  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42047  */
42048 Roo.bootstrap.panel.TabItem = function(config){
42049     /**
42050      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42051      * @type Roo.TabPanel
42052      */
42053     this.tabPanel = config.panel;
42054     /**
42055      * The id for this TabPanelItem
42056      * @type String
42057      */
42058     this.id = config.id;
42059     /** @private */
42060     this.disabled = false;
42061     /** @private */
42062     this.text = config.text;
42063     /** @private */
42064     this.loaded = false;
42065     this.closable = config.closable;
42066
42067     /**
42068      * The body element for this TabPanelItem.
42069      * @type Roo.Element
42070      */
42071     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42072     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42073     this.bodyEl.setStyle("display", "block");
42074     this.bodyEl.setStyle("zoom", "1");
42075     //this.hideAction();
42076
42077     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42078     /** @private */
42079     this.el = Roo.get(els.el);
42080     this.inner = Roo.get(els.inner, true);
42081      this.textEl = Roo.bootstrap.version == 4 ?
42082         this.el : Roo.get(this.el.dom.firstChild, true);
42083
42084     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42085     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42086
42087     
42088 //    this.el.on("mousedown", this.onTabMouseDown, this);
42089     this.el.on("click", this.onTabClick, this);
42090     /** @private */
42091     if(config.closable){
42092         var c = Roo.get(els.close, true);
42093         c.dom.title = this.closeText;
42094         c.addClassOnOver("close-over");
42095         c.on("click", this.closeClick, this);
42096      }
42097
42098     this.addEvents({
42099          /**
42100          * @event activate
42101          * Fires when this tab becomes the active tab.
42102          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42103          * @param {Roo.TabPanelItem} this
42104          */
42105         "activate": true,
42106         /**
42107          * @event beforeclose
42108          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42109          * @param {Roo.TabPanelItem} this
42110          * @param {Object} e Set cancel to true on this object to cancel the close.
42111          */
42112         "beforeclose": true,
42113         /**
42114          * @event close
42115          * Fires when this tab is closed.
42116          * @param {Roo.TabPanelItem} this
42117          */
42118          "close": true,
42119         /**
42120          * @event deactivate
42121          * Fires when this tab is no longer the active tab.
42122          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42123          * @param {Roo.TabPanelItem} this
42124          */
42125          "deactivate" : true
42126     });
42127     this.hidden = false;
42128
42129     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42130 };
42131
42132 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42133            {
42134     purgeListeners : function(){
42135        Roo.util.Observable.prototype.purgeListeners.call(this);
42136        this.el.removeAllListeners();
42137     },
42138     /**
42139      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42140      */
42141     show : function(){
42142         this.status_node.addClass("active");
42143         this.showAction();
42144         if(Roo.isOpera){
42145             this.tabPanel.stripWrap.repaint();
42146         }
42147         this.fireEvent("activate", this.tabPanel, this);
42148     },
42149
42150     /**
42151      * Returns true if this tab is the active tab.
42152      * @return {Boolean}
42153      */
42154     isActive : function(){
42155         return this.tabPanel.getActiveTab() == this;
42156     },
42157
42158     /**
42159      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42160      */
42161     hide : function(){
42162         this.status_node.removeClass("active");
42163         this.hideAction();
42164         this.fireEvent("deactivate", this.tabPanel, this);
42165     },
42166
42167     hideAction : function(){
42168         this.bodyEl.hide();
42169         this.bodyEl.setStyle("position", "absolute");
42170         this.bodyEl.setLeft("-20000px");
42171         this.bodyEl.setTop("-20000px");
42172     },
42173
42174     showAction : function(){
42175         this.bodyEl.setStyle("position", "relative");
42176         this.bodyEl.setTop("");
42177         this.bodyEl.setLeft("");
42178         this.bodyEl.show();
42179     },
42180
42181     /**
42182      * Set the tooltip for the tab.
42183      * @param {String} tooltip The tab's tooltip
42184      */
42185     setTooltip : function(text){
42186         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42187             this.textEl.dom.qtip = text;
42188             this.textEl.dom.removeAttribute('title');
42189         }else{
42190             this.textEl.dom.title = text;
42191         }
42192     },
42193
42194     onTabClick : function(e){
42195         e.preventDefault();
42196         this.tabPanel.activate(this.id);
42197     },
42198
42199     onTabMouseDown : function(e){
42200         e.preventDefault();
42201         this.tabPanel.activate(this.id);
42202     },
42203 /*
42204     getWidth : function(){
42205         return this.inner.getWidth();
42206     },
42207
42208     setWidth : function(width){
42209         var iwidth = width - this.linode.getPadding("lr");
42210         this.inner.setWidth(iwidth);
42211         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42212         this.linode.setWidth(width);
42213     },
42214 */
42215     /**
42216      * Show or hide the tab
42217      * @param {Boolean} hidden True to hide or false to show.
42218      */
42219     setHidden : function(hidden){
42220         this.hidden = hidden;
42221         this.linode.setStyle("display", hidden ? "none" : "");
42222     },
42223
42224     /**
42225      * Returns true if this tab is "hidden"
42226      * @return {Boolean}
42227      */
42228     isHidden : function(){
42229         return this.hidden;
42230     },
42231
42232     /**
42233      * Returns the text for this tab
42234      * @return {String}
42235      */
42236     getText : function(){
42237         return this.text;
42238     },
42239     /*
42240     autoSize : function(){
42241         //this.el.beginMeasure();
42242         this.textEl.setWidth(1);
42243         /*
42244          *  #2804 [new] Tabs in Roojs
42245          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42246          */
42247         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42248         //this.el.endMeasure();
42249     //},
42250
42251     /**
42252      * Sets the text for the tab (Note: this also sets the tooltip text)
42253      * @param {String} text The tab's text and tooltip
42254      */
42255     setText : function(text){
42256         this.text = text;
42257         this.textEl.update(text);
42258         this.setTooltip(text);
42259         //if(!this.tabPanel.resizeTabs){
42260         //    this.autoSize();
42261         //}
42262     },
42263     /**
42264      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42265      */
42266     activate : function(){
42267         this.tabPanel.activate(this.id);
42268     },
42269
42270     /**
42271      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42272      */
42273     disable : function(){
42274         if(this.tabPanel.active != this){
42275             this.disabled = true;
42276             this.status_node.addClass("disabled");
42277         }
42278     },
42279
42280     /**
42281      * Enables this TabPanelItem if it was previously disabled.
42282      */
42283     enable : function(){
42284         this.disabled = false;
42285         this.status_node.removeClass("disabled");
42286     },
42287
42288     /**
42289      * Sets the content for this TabPanelItem.
42290      * @param {String} content The content
42291      * @param {Boolean} loadScripts true to look for and load scripts
42292      */
42293     setContent : function(content, loadScripts){
42294         this.bodyEl.update(content, loadScripts);
42295     },
42296
42297     /**
42298      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42299      * @return {Roo.UpdateManager} The UpdateManager
42300      */
42301     getUpdateManager : function(){
42302         return this.bodyEl.getUpdateManager();
42303     },
42304
42305     /**
42306      * Set a URL to be used to load the content for this TabPanelItem.
42307      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42308      * @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)
42309      * @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)
42310      * @return {Roo.UpdateManager} The UpdateManager
42311      */
42312     setUrl : function(url, params, loadOnce){
42313         if(this.refreshDelegate){
42314             this.un('activate', this.refreshDelegate);
42315         }
42316         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42317         this.on("activate", this.refreshDelegate);
42318         return this.bodyEl.getUpdateManager();
42319     },
42320
42321     /** @private */
42322     _handleRefresh : function(url, params, loadOnce){
42323         if(!loadOnce || !this.loaded){
42324             var updater = this.bodyEl.getUpdateManager();
42325             updater.update(url, params, this._setLoaded.createDelegate(this));
42326         }
42327     },
42328
42329     /**
42330      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42331      *   Will fail silently if the setUrl method has not been called.
42332      *   This does not activate the panel, just updates its content.
42333      */
42334     refresh : function(){
42335         if(this.refreshDelegate){
42336            this.loaded = false;
42337            this.refreshDelegate();
42338         }
42339     },
42340
42341     /** @private */
42342     _setLoaded : function(){
42343         this.loaded = true;
42344     },
42345
42346     /** @private */
42347     closeClick : function(e){
42348         var o = {};
42349         e.stopEvent();
42350         this.fireEvent("beforeclose", this, o);
42351         if(o.cancel !== true){
42352             this.tabPanel.removeTab(this.id);
42353         }
42354     },
42355     /**
42356      * The text displayed in the tooltip for the close icon.
42357      * @type String
42358      */
42359     closeText : "Close this tab"
42360 });
42361 /**
42362 *    This script refer to:
42363 *    Title: International Telephone Input
42364 *    Author: Jack O'Connor
42365 *    Code version:  v12.1.12
42366 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42367 **/
42368
42369 Roo.bootstrap.PhoneInputData = function() {
42370     var d = [
42371       [
42372         "Afghanistan (‫افغانستان‬‎)",
42373         "af",
42374         "93"
42375       ],
42376       [
42377         "Albania (Shqipëri)",
42378         "al",
42379         "355"
42380       ],
42381       [
42382         "Algeria (‫الجزائر‬‎)",
42383         "dz",
42384         "213"
42385       ],
42386       [
42387         "American Samoa",
42388         "as",
42389         "1684"
42390       ],
42391       [
42392         "Andorra",
42393         "ad",
42394         "376"
42395       ],
42396       [
42397         "Angola",
42398         "ao",
42399         "244"
42400       ],
42401       [
42402         "Anguilla",
42403         "ai",
42404         "1264"
42405       ],
42406       [
42407         "Antigua and Barbuda",
42408         "ag",
42409         "1268"
42410       ],
42411       [
42412         "Argentina",
42413         "ar",
42414         "54"
42415       ],
42416       [
42417         "Armenia (Հայաստան)",
42418         "am",
42419         "374"
42420       ],
42421       [
42422         "Aruba",
42423         "aw",
42424         "297"
42425       ],
42426       [
42427         "Australia",
42428         "au",
42429         "61",
42430         0
42431       ],
42432       [
42433         "Austria (Österreich)",
42434         "at",
42435         "43"
42436       ],
42437       [
42438         "Azerbaijan (Azərbaycan)",
42439         "az",
42440         "994"
42441       ],
42442       [
42443         "Bahamas",
42444         "bs",
42445         "1242"
42446       ],
42447       [
42448         "Bahrain (‫البحرين‬‎)",
42449         "bh",
42450         "973"
42451       ],
42452       [
42453         "Bangladesh (বাংলাদেশ)",
42454         "bd",
42455         "880"
42456       ],
42457       [
42458         "Barbados",
42459         "bb",
42460         "1246"
42461       ],
42462       [
42463         "Belarus (Беларусь)",
42464         "by",
42465         "375"
42466       ],
42467       [
42468         "Belgium (België)",
42469         "be",
42470         "32"
42471       ],
42472       [
42473         "Belize",
42474         "bz",
42475         "501"
42476       ],
42477       [
42478         "Benin (Bénin)",
42479         "bj",
42480         "229"
42481       ],
42482       [
42483         "Bermuda",
42484         "bm",
42485         "1441"
42486       ],
42487       [
42488         "Bhutan (འབྲུག)",
42489         "bt",
42490         "975"
42491       ],
42492       [
42493         "Bolivia",
42494         "bo",
42495         "591"
42496       ],
42497       [
42498         "Bosnia and Herzegovina (Босна и Херцеговина)",
42499         "ba",
42500         "387"
42501       ],
42502       [
42503         "Botswana",
42504         "bw",
42505         "267"
42506       ],
42507       [
42508         "Brazil (Brasil)",
42509         "br",
42510         "55"
42511       ],
42512       [
42513         "British Indian Ocean Territory",
42514         "io",
42515         "246"
42516       ],
42517       [
42518         "British Virgin Islands",
42519         "vg",
42520         "1284"
42521       ],
42522       [
42523         "Brunei",
42524         "bn",
42525         "673"
42526       ],
42527       [
42528         "Bulgaria (България)",
42529         "bg",
42530         "359"
42531       ],
42532       [
42533         "Burkina Faso",
42534         "bf",
42535         "226"
42536       ],
42537       [
42538         "Burundi (Uburundi)",
42539         "bi",
42540         "257"
42541       ],
42542       [
42543         "Cambodia (កម្ពុជា)",
42544         "kh",
42545         "855"
42546       ],
42547       [
42548         "Cameroon (Cameroun)",
42549         "cm",
42550         "237"
42551       ],
42552       [
42553         "Canada",
42554         "ca",
42555         "1",
42556         1,
42557         ["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"]
42558       ],
42559       [
42560         "Cape Verde (Kabu Verdi)",
42561         "cv",
42562         "238"
42563       ],
42564       [
42565         "Caribbean Netherlands",
42566         "bq",
42567         "599",
42568         1
42569       ],
42570       [
42571         "Cayman Islands",
42572         "ky",
42573         "1345"
42574       ],
42575       [
42576         "Central African Republic (République centrafricaine)",
42577         "cf",
42578         "236"
42579       ],
42580       [
42581         "Chad (Tchad)",
42582         "td",
42583         "235"
42584       ],
42585       [
42586         "Chile",
42587         "cl",
42588         "56"
42589       ],
42590       [
42591         "China (中国)",
42592         "cn",
42593         "86"
42594       ],
42595       [
42596         "Christmas Island",
42597         "cx",
42598         "61",
42599         2
42600       ],
42601       [
42602         "Cocos (Keeling) Islands",
42603         "cc",
42604         "61",
42605         1
42606       ],
42607       [
42608         "Colombia",
42609         "co",
42610         "57"
42611       ],
42612       [
42613         "Comoros (‫جزر القمر‬‎)",
42614         "km",
42615         "269"
42616       ],
42617       [
42618         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42619         "cd",
42620         "243"
42621       ],
42622       [
42623         "Congo (Republic) (Congo-Brazzaville)",
42624         "cg",
42625         "242"
42626       ],
42627       [
42628         "Cook Islands",
42629         "ck",
42630         "682"
42631       ],
42632       [
42633         "Costa Rica",
42634         "cr",
42635         "506"
42636       ],
42637       [
42638         "Côte d’Ivoire",
42639         "ci",
42640         "225"
42641       ],
42642       [
42643         "Croatia (Hrvatska)",
42644         "hr",
42645         "385"
42646       ],
42647       [
42648         "Cuba",
42649         "cu",
42650         "53"
42651       ],
42652       [
42653         "Curaçao",
42654         "cw",
42655         "599",
42656         0
42657       ],
42658       [
42659         "Cyprus (Κύπρος)",
42660         "cy",
42661         "357"
42662       ],
42663       [
42664         "Czech Republic (Česká republika)",
42665         "cz",
42666         "420"
42667       ],
42668       [
42669         "Denmark (Danmark)",
42670         "dk",
42671         "45"
42672       ],
42673       [
42674         "Djibouti",
42675         "dj",
42676         "253"
42677       ],
42678       [
42679         "Dominica",
42680         "dm",
42681         "1767"
42682       ],
42683       [
42684         "Dominican Republic (República Dominicana)",
42685         "do",
42686         "1",
42687         2,
42688         ["809", "829", "849"]
42689       ],
42690       [
42691         "Ecuador",
42692         "ec",
42693         "593"
42694       ],
42695       [
42696         "Egypt (‫مصر‬‎)",
42697         "eg",
42698         "20"
42699       ],
42700       [
42701         "El Salvador",
42702         "sv",
42703         "503"
42704       ],
42705       [
42706         "Equatorial Guinea (Guinea Ecuatorial)",
42707         "gq",
42708         "240"
42709       ],
42710       [
42711         "Eritrea",
42712         "er",
42713         "291"
42714       ],
42715       [
42716         "Estonia (Eesti)",
42717         "ee",
42718         "372"
42719       ],
42720       [
42721         "Ethiopia",
42722         "et",
42723         "251"
42724       ],
42725       [
42726         "Falkland Islands (Islas Malvinas)",
42727         "fk",
42728         "500"
42729       ],
42730       [
42731         "Faroe Islands (Føroyar)",
42732         "fo",
42733         "298"
42734       ],
42735       [
42736         "Fiji",
42737         "fj",
42738         "679"
42739       ],
42740       [
42741         "Finland (Suomi)",
42742         "fi",
42743         "358",
42744         0
42745       ],
42746       [
42747         "France",
42748         "fr",
42749         "33"
42750       ],
42751       [
42752         "French Guiana (Guyane française)",
42753         "gf",
42754         "594"
42755       ],
42756       [
42757         "French Polynesia (Polynésie française)",
42758         "pf",
42759         "689"
42760       ],
42761       [
42762         "Gabon",
42763         "ga",
42764         "241"
42765       ],
42766       [
42767         "Gambia",
42768         "gm",
42769         "220"
42770       ],
42771       [
42772         "Georgia (საქართველო)",
42773         "ge",
42774         "995"
42775       ],
42776       [
42777         "Germany (Deutschland)",
42778         "de",
42779         "49"
42780       ],
42781       [
42782         "Ghana (Gaana)",
42783         "gh",
42784         "233"
42785       ],
42786       [
42787         "Gibraltar",
42788         "gi",
42789         "350"
42790       ],
42791       [
42792         "Greece (Ελλάδα)",
42793         "gr",
42794         "30"
42795       ],
42796       [
42797         "Greenland (Kalaallit Nunaat)",
42798         "gl",
42799         "299"
42800       ],
42801       [
42802         "Grenada",
42803         "gd",
42804         "1473"
42805       ],
42806       [
42807         "Guadeloupe",
42808         "gp",
42809         "590",
42810         0
42811       ],
42812       [
42813         "Guam",
42814         "gu",
42815         "1671"
42816       ],
42817       [
42818         "Guatemala",
42819         "gt",
42820         "502"
42821       ],
42822       [
42823         "Guernsey",
42824         "gg",
42825         "44",
42826         1
42827       ],
42828       [
42829         "Guinea (Guinée)",
42830         "gn",
42831         "224"
42832       ],
42833       [
42834         "Guinea-Bissau (Guiné Bissau)",
42835         "gw",
42836         "245"
42837       ],
42838       [
42839         "Guyana",
42840         "gy",
42841         "592"
42842       ],
42843       [
42844         "Haiti",
42845         "ht",
42846         "509"
42847       ],
42848       [
42849         "Honduras",
42850         "hn",
42851         "504"
42852       ],
42853       [
42854         "Hong Kong (香港)",
42855         "hk",
42856         "852"
42857       ],
42858       [
42859         "Hungary (Magyarország)",
42860         "hu",
42861         "36"
42862       ],
42863       [
42864         "Iceland (Ísland)",
42865         "is",
42866         "354"
42867       ],
42868       [
42869         "India (भारत)",
42870         "in",
42871         "91"
42872       ],
42873       [
42874         "Indonesia",
42875         "id",
42876         "62"
42877       ],
42878       [
42879         "Iran (‫ایران‬‎)",
42880         "ir",
42881         "98"
42882       ],
42883       [
42884         "Iraq (‫العراق‬‎)",
42885         "iq",
42886         "964"
42887       ],
42888       [
42889         "Ireland",
42890         "ie",
42891         "353"
42892       ],
42893       [
42894         "Isle of Man",
42895         "im",
42896         "44",
42897         2
42898       ],
42899       [
42900         "Israel (‫ישראל‬‎)",
42901         "il",
42902         "972"
42903       ],
42904       [
42905         "Italy (Italia)",
42906         "it",
42907         "39",
42908         0
42909       ],
42910       [
42911         "Jamaica",
42912         "jm",
42913         "1876"
42914       ],
42915       [
42916         "Japan (日本)",
42917         "jp",
42918         "81"
42919       ],
42920       [
42921         "Jersey",
42922         "je",
42923         "44",
42924         3
42925       ],
42926       [
42927         "Jordan (‫الأردن‬‎)",
42928         "jo",
42929         "962"
42930       ],
42931       [
42932         "Kazakhstan (Казахстан)",
42933         "kz",
42934         "7",
42935         1
42936       ],
42937       [
42938         "Kenya",
42939         "ke",
42940         "254"
42941       ],
42942       [
42943         "Kiribati",
42944         "ki",
42945         "686"
42946       ],
42947       [
42948         "Kosovo",
42949         "xk",
42950         "383"
42951       ],
42952       [
42953         "Kuwait (‫الكويت‬‎)",
42954         "kw",
42955         "965"
42956       ],
42957       [
42958         "Kyrgyzstan (Кыргызстан)",
42959         "kg",
42960         "996"
42961       ],
42962       [
42963         "Laos (ລາວ)",
42964         "la",
42965         "856"
42966       ],
42967       [
42968         "Latvia (Latvija)",
42969         "lv",
42970         "371"
42971       ],
42972       [
42973         "Lebanon (‫لبنان‬‎)",
42974         "lb",
42975         "961"
42976       ],
42977       [
42978         "Lesotho",
42979         "ls",
42980         "266"
42981       ],
42982       [
42983         "Liberia",
42984         "lr",
42985         "231"
42986       ],
42987       [
42988         "Libya (‫ليبيا‬‎)",
42989         "ly",
42990         "218"
42991       ],
42992       [
42993         "Liechtenstein",
42994         "li",
42995         "423"
42996       ],
42997       [
42998         "Lithuania (Lietuva)",
42999         "lt",
43000         "370"
43001       ],
43002       [
43003         "Luxembourg",
43004         "lu",
43005         "352"
43006       ],
43007       [
43008         "Macau (澳門)",
43009         "mo",
43010         "853"
43011       ],
43012       [
43013         "Macedonia (FYROM) (Македонија)",
43014         "mk",
43015         "389"
43016       ],
43017       [
43018         "Madagascar (Madagasikara)",
43019         "mg",
43020         "261"
43021       ],
43022       [
43023         "Malawi",
43024         "mw",
43025         "265"
43026       ],
43027       [
43028         "Malaysia",
43029         "my",
43030         "60"
43031       ],
43032       [
43033         "Maldives",
43034         "mv",
43035         "960"
43036       ],
43037       [
43038         "Mali",
43039         "ml",
43040         "223"
43041       ],
43042       [
43043         "Malta",
43044         "mt",
43045         "356"
43046       ],
43047       [
43048         "Marshall Islands",
43049         "mh",
43050         "692"
43051       ],
43052       [
43053         "Martinique",
43054         "mq",
43055         "596"
43056       ],
43057       [
43058         "Mauritania (‫موريتانيا‬‎)",
43059         "mr",
43060         "222"
43061       ],
43062       [
43063         "Mauritius (Moris)",
43064         "mu",
43065         "230"
43066       ],
43067       [
43068         "Mayotte",
43069         "yt",
43070         "262",
43071         1
43072       ],
43073       [
43074         "Mexico (México)",
43075         "mx",
43076         "52"
43077       ],
43078       [
43079         "Micronesia",
43080         "fm",
43081         "691"
43082       ],
43083       [
43084         "Moldova (Republica Moldova)",
43085         "md",
43086         "373"
43087       ],
43088       [
43089         "Monaco",
43090         "mc",
43091         "377"
43092       ],
43093       [
43094         "Mongolia (Монгол)",
43095         "mn",
43096         "976"
43097       ],
43098       [
43099         "Montenegro (Crna Gora)",
43100         "me",
43101         "382"
43102       ],
43103       [
43104         "Montserrat",
43105         "ms",
43106         "1664"
43107       ],
43108       [
43109         "Morocco (‫المغرب‬‎)",
43110         "ma",
43111         "212",
43112         0
43113       ],
43114       [
43115         "Mozambique (Moçambique)",
43116         "mz",
43117         "258"
43118       ],
43119       [
43120         "Myanmar (Burma) (မြန်မာ)",
43121         "mm",
43122         "95"
43123       ],
43124       [
43125         "Namibia (Namibië)",
43126         "na",
43127         "264"
43128       ],
43129       [
43130         "Nauru",
43131         "nr",
43132         "674"
43133       ],
43134       [
43135         "Nepal (नेपाल)",
43136         "np",
43137         "977"
43138       ],
43139       [
43140         "Netherlands (Nederland)",
43141         "nl",
43142         "31"
43143       ],
43144       [
43145         "New Caledonia (Nouvelle-Calédonie)",
43146         "nc",
43147         "687"
43148       ],
43149       [
43150         "New Zealand",
43151         "nz",
43152         "64"
43153       ],
43154       [
43155         "Nicaragua",
43156         "ni",
43157         "505"
43158       ],
43159       [
43160         "Niger (Nijar)",
43161         "ne",
43162         "227"
43163       ],
43164       [
43165         "Nigeria",
43166         "ng",
43167         "234"
43168       ],
43169       [
43170         "Niue",
43171         "nu",
43172         "683"
43173       ],
43174       [
43175         "Norfolk Island",
43176         "nf",
43177         "672"
43178       ],
43179       [
43180         "North Korea (조선 민주주의 인민 공화국)",
43181         "kp",
43182         "850"
43183       ],
43184       [
43185         "Northern Mariana Islands",
43186         "mp",
43187         "1670"
43188       ],
43189       [
43190         "Norway (Norge)",
43191         "no",
43192         "47",
43193         0
43194       ],
43195       [
43196         "Oman (‫عُمان‬‎)",
43197         "om",
43198         "968"
43199       ],
43200       [
43201         "Pakistan (‫پاکستان‬‎)",
43202         "pk",
43203         "92"
43204       ],
43205       [
43206         "Palau",
43207         "pw",
43208         "680"
43209       ],
43210       [
43211         "Palestine (‫فلسطين‬‎)",
43212         "ps",
43213         "970"
43214       ],
43215       [
43216         "Panama (Panamá)",
43217         "pa",
43218         "507"
43219       ],
43220       [
43221         "Papua New Guinea",
43222         "pg",
43223         "675"
43224       ],
43225       [
43226         "Paraguay",
43227         "py",
43228         "595"
43229       ],
43230       [
43231         "Peru (Perú)",
43232         "pe",
43233         "51"
43234       ],
43235       [
43236         "Philippines",
43237         "ph",
43238         "63"
43239       ],
43240       [
43241         "Poland (Polska)",
43242         "pl",
43243         "48"
43244       ],
43245       [
43246         "Portugal",
43247         "pt",
43248         "351"
43249       ],
43250       [
43251         "Puerto Rico",
43252         "pr",
43253         "1",
43254         3,
43255         ["787", "939"]
43256       ],
43257       [
43258         "Qatar (‫قطر‬‎)",
43259         "qa",
43260         "974"
43261       ],
43262       [
43263         "Réunion (La Réunion)",
43264         "re",
43265         "262",
43266         0
43267       ],
43268       [
43269         "Romania (România)",
43270         "ro",
43271         "40"
43272       ],
43273       [
43274         "Russia (Россия)",
43275         "ru",
43276         "7",
43277         0
43278       ],
43279       [
43280         "Rwanda",
43281         "rw",
43282         "250"
43283       ],
43284       [
43285         "Saint Barthélemy",
43286         "bl",
43287         "590",
43288         1
43289       ],
43290       [
43291         "Saint Helena",
43292         "sh",
43293         "290"
43294       ],
43295       [
43296         "Saint Kitts and Nevis",
43297         "kn",
43298         "1869"
43299       ],
43300       [
43301         "Saint Lucia",
43302         "lc",
43303         "1758"
43304       ],
43305       [
43306         "Saint Martin (Saint-Martin (partie française))",
43307         "mf",
43308         "590",
43309         2
43310       ],
43311       [
43312         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43313         "pm",
43314         "508"
43315       ],
43316       [
43317         "Saint Vincent and the Grenadines",
43318         "vc",
43319         "1784"
43320       ],
43321       [
43322         "Samoa",
43323         "ws",
43324         "685"
43325       ],
43326       [
43327         "San Marino",
43328         "sm",
43329         "378"
43330       ],
43331       [
43332         "São Tomé and Príncipe (São Tomé e Príncipe)",
43333         "st",
43334         "239"
43335       ],
43336       [
43337         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43338         "sa",
43339         "966"
43340       ],
43341       [
43342         "Senegal (Sénégal)",
43343         "sn",
43344         "221"
43345       ],
43346       [
43347         "Serbia (Србија)",
43348         "rs",
43349         "381"
43350       ],
43351       [
43352         "Seychelles",
43353         "sc",
43354         "248"
43355       ],
43356       [
43357         "Sierra Leone",
43358         "sl",
43359         "232"
43360       ],
43361       [
43362         "Singapore",
43363         "sg",
43364         "65"
43365       ],
43366       [
43367         "Sint Maarten",
43368         "sx",
43369         "1721"
43370       ],
43371       [
43372         "Slovakia (Slovensko)",
43373         "sk",
43374         "421"
43375       ],
43376       [
43377         "Slovenia (Slovenija)",
43378         "si",
43379         "386"
43380       ],
43381       [
43382         "Solomon Islands",
43383         "sb",
43384         "677"
43385       ],
43386       [
43387         "Somalia (Soomaaliya)",
43388         "so",
43389         "252"
43390       ],
43391       [
43392         "South Africa",
43393         "za",
43394         "27"
43395       ],
43396       [
43397         "South Korea (대한민국)",
43398         "kr",
43399         "82"
43400       ],
43401       [
43402         "South Sudan (‫جنوب السودان‬‎)",
43403         "ss",
43404         "211"
43405       ],
43406       [
43407         "Spain (España)",
43408         "es",
43409         "34"
43410       ],
43411       [
43412         "Sri Lanka (ශ්‍රී ලංකාව)",
43413         "lk",
43414         "94"
43415       ],
43416       [
43417         "Sudan (‫السودان‬‎)",
43418         "sd",
43419         "249"
43420       ],
43421       [
43422         "Suriname",
43423         "sr",
43424         "597"
43425       ],
43426       [
43427         "Svalbard and Jan Mayen",
43428         "sj",
43429         "47",
43430         1
43431       ],
43432       [
43433         "Swaziland",
43434         "sz",
43435         "268"
43436       ],
43437       [
43438         "Sweden (Sverige)",
43439         "se",
43440         "46"
43441       ],
43442       [
43443         "Switzerland (Schweiz)",
43444         "ch",
43445         "41"
43446       ],
43447       [
43448         "Syria (‫سوريا‬‎)",
43449         "sy",
43450         "963"
43451       ],
43452       [
43453         "Taiwan (台灣)",
43454         "tw",
43455         "886"
43456       ],
43457       [
43458         "Tajikistan",
43459         "tj",
43460         "992"
43461       ],
43462       [
43463         "Tanzania",
43464         "tz",
43465         "255"
43466       ],
43467       [
43468         "Thailand (ไทย)",
43469         "th",
43470         "66"
43471       ],
43472       [
43473         "Timor-Leste",
43474         "tl",
43475         "670"
43476       ],
43477       [
43478         "Togo",
43479         "tg",
43480         "228"
43481       ],
43482       [
43483         "Tokelau",
43484         "tk",
43485         "690"
43486       ],
43487       [
43488         "Tonga",
43489         "to",
43490         "676"
43491       ],
43492       [
43493         "Trinidad and Tobago",
43494         "tt",
43495         "1868"
43496       ],
43497       [
43498         "Tunisia (‫تونس‬‎)",
43499         "tn",
43500         "216"
43501       ],
43502       [
43503         "Turkey (Türkiye)",
43504         "tr",
43505         "90"
43506       ],
43507       [
43508         "Turkmenistan",
43509         "tm",
43510         "993"
43511       ],
43512       [
43513         "Turks and Caicos Islands",
43514         "tc",
43515         "1649"
43516       ],
43517       [
43518         "Tuvalu",
43519         "tv",
43520         "688"
43521       ],
43522       [
43523         "U.S. Virgin Islands",
43524         "vi",
43525         "1340"
43526       ],
43527       [
43528         "Uganda",
43529         "ug",
43530         "256"
43531       ],
43532       [
43533         "Ukraine (Україна)",
43534         "ua",
43535         "380"
43536       ],
43537       [
43538         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43539         "ae",
43540         "971"
43541       ],
43542       [
43543         "United Kingdom",
43544         "gb",
43545         "44",
43546         0
43547       ],
43548       [
43549         "United States",
43550         "us",
43551         "1",
43552         0
43553       ],
43554       [
43555         "Uruguay",
43556         "uy",
43557         "598"
43558       ],
43559       [
43560         "Uzbekistan (Oʻzbekiston)",
43561         "uz",
43562         "998"
43563       ],
43564       [
43565         "Vanuatu",
43566         "vu",
43567         "678"
43568       ],
43569       [
43570         "Vatican City (Città del Vaticano)",
43571         "va",
43572         "39",
43573         1
43574       ],
43575       [
43576         "Venezuela",
43577         "ve",
43578         "58"
43579       ],
43580       [
43581         "Vietnam (Việt Nam)",
43582         "vn",
43583         "84"
43584       ],
43585       [
43586         "Wallis and Futuna (Wallis-et-Futuna)",
43587         "wf",
43588         "681"
43589       ],
43590       [
43591         "Western Sahara (‫الصحراء الغربية‬‎)",
43592         "eh",
43593         "212",
43594         1
43595       ],
43596       [
43597         "Yemen (‫اليمن‬‎)",
43598         "ye",
43599         "967"
43600       ],
43601       [
43602         "Zambia",
43603         "zm",
43604         "260"
43605       ],
43606       [
43607         "Zimbabwe",
43608         "zw",
43609         "263"
43610       ],
43611       [
43612         "Åland Islands",
43613         "ax",
43614         "358",
43615         1
43616       ]
43617   ];
43618   
43619   return d;
43620 }/**
43621 *    This script refer to:
43622 *    Title: International Telephone Input
43623 *    Author: Jack O'Connor
43624 *    Code version:  v12.1.12
43625 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43626 **/
43627
43628 /**
43629  * @class Roo.bootstrap.PhoneInput
43630  * @extends Roo.bootstrap.TriggerField
43631  * An input with International dial-code selection
43632  
43633  * @cfg {String} defaultDialCode default '+852'
43634  * @cfg {Array} preferedCountries default []
43635   
43636  * @constructor
43637  * Create a new PhoneInput.
43638  * @param {Object} config Configuration options
43639  */
43640
43641 Roo.bootstrap.PhoneInput = function(config) {
43642     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43643 };
43644
43645 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43646         /**
43647         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43648         */
43649         listWidth: undefined,
43650         
43651         selectedClass: 'active',
43652         
43653         invalidClass : "has-warning",
43654         
43655         validClass: 'has-success',
43656         
43657         allowed: '0123456789',
43658         
43659         max_length: 15,
43660         
43661         /**
43662          * @cfg {String} defaultDialCode The default dial code when initializing the input
43663          */
43664         defaultDialCode: '+852',
43665         
43666         /**
43667          * @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
43668          */
43669         preferedCountries: false,
43670         
43671         getAutoCreate : function()
43672         {
43673             var data = Roo.bootstrap.PhoneInputData();
43674             var align = this.labelAlign || this.parentLabelAlign();
43675             var id = Roo.id();
43676             
43677             this.allCountries = [];
43678             this.dialCodeMapping = [];
43679             
43680             for (var i = 0; i < data.length; i++) {
43681               var c = data[i];
43682               this.allCountries[i] = {
43683                 name: c[0],
43684                 iso2: c[1],
43685                 dialCode: c[2],
43686                 priority: c[3] || 0,
43687                 areaCodes: c[4] || null
43688               };
43689               this.dialCodeMapping[c[2]] = {
43690                   name: c[0],
43691                   iso2: c[1],
43692                   priority: c[3] || 0,
43693                   areaCodes: c[4] || null
43694               };
43695             }
43696             
43697             var cfg = {
43698                 cls: 'form-group',
43699                 cn: []
43700             };
43701             
43702             var input =  {
43703                 tag: 'input',
43704                 id : id,
43705                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43706                 maxlength: this.max_length,
43707                 cls : 'form-control tel-input',
43708                 autocomplete: 'new-password'
43709             };
43710             
43711             var hiddenInput = {
43712                 tag: 'input',
43713                 type: 'hidden',
43714                 cls: 'hidden-tel-input'
43715             };
43716             
43717             if (this.name) {
43718                 hiddenInput.name = this.name;
43719             }
43720             
43721             if (this.disabled) {
43722                 input.disabled = true;
43723             }
43724             
43725             var flag_container = {
43726                 tag: 'div',
43727                 cls: 'flag-box',
43728                 cn: [
43729                     {
43730                         tag: 'div',
43731                         cls: 'flag'
43732                     },
43733                     {
43734                         tag: 'div',
43735                         cls: 'caret'
43736                     }
43737                 ]
43738             };
43739             
43740             var box = {
43741                 tag: 'div',
43742                 cls: this.hasFeedback ? 'has-feedback' : '',
43743                 cn: [
43744                     hiddenInput,
43745                     input,
43746                     {
43747                         tag: 'input',
43748                         cls: 'dial-code-holder',
43749                         disabled: true
43750                     }
43751                 ]
43752             };
43753             
43754             var container = {
43755                 cls: 'roo-select2-container input-group',
43756                 cn: [
43757                     flag_container,
43758                     box
43759                 ]
43760             };
43761             
43762             if (this.fieldLabel.length) {
43763                 var indicator = {
43764                     tag: 'i',
43765                     tooltip: 'This field is required'
43766                 };
43767                 
43768                 var label = {
43769                     tag: 'label',
43770                     'for':  id,
43771                     cls: 'control-label',
43772                     cn: []
43773                 };
43774                 
43775                 var label_text = {
43776                     tag: 'span',
43777                     html: this.fieldLabel
43778                 };
43779                 
43780                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43781                 label.cn = [
43782                     indicator,
43783                     label_text
43784                 ];
43785                 
43786                 if(this.indicatorpos == 'right') {
43787                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43788                     label.cn = [
43789                         label_text,
43790                         indicator
43791                     ];
43792                 }
43793                 
43794                 if(align == 'left') {
43795                     container = {
43796                         tag: 'div',
43797                         cn: [
43798                             container
43799                         ]
43800                     };
43801                     
43802                     if(this.labelWidth > 12){
43803                         label.style = "width: " + this.labelWidth + 'px';
43804                     }
43805                     if(this.labelWidth < 13 && this.labelmd == 0){
43806                         this.labelmd = this.labelWidth;
43807                     }
43808                     if(this.labellg > 0){
43809                         label.cls += ' col-lg-' + this.labellg;
43810                         input.cls += ' col-lg-' + (12 - this.labellg);
43811                     }
43812                     if(this.labelmd > 0){
43813                         label.cls += ' col-md-' + this.labelmd;
43814                         container.cls += ' col-md-' + (12 - this.labelmd);
43815                     }
43816                     if(this.labelsm > 0){
43817                         label.cls += ' col-sm-' + this.labelsm;
43818                         container.cls += ' col-sm-' + (12 - this.labelsm);
43819                     }
43820                     if(this.labelxs > 0){
43821                         label.cls += ' col-xs-' + this.labelxs;
43822                         container.cls += ' col-xs-' + (12 - this.labelxs);
43823                     }
43824                 }
43825             }
43826             
43827             cfg.cn = [
43828                 label,
43829                 container
43830             ];
43831             
43832             var settings = this;
43833             
43834             ['xs','sm','md','lg'].map(function(size){
43835                 if (settings[size]) {
43836                     cfg.cls += ' col-' + size + '-' + settings[size];
43837                 }
43838             });
43839             
43840             this.store = new Roo.data.Store({
43841                 proxy : new Roo.data.MemoryProxy({}),
43842                 reader : new Roo.data.JsonReader({
43843                     fields : [
43844                         {
43845                             'name' : 'name',
43846                             'type' : 'string'
43847                         },
43848                         {
43849                             'name' : 'iso2',
43850                             'type' : 'string'
43851                         },
43852                         {
43853                             'name' : 'dialCode',
43854                             'type' : 'string'
43855                         },
43856                         {
43857                             'name' : 'priority',
43858                             'type' : 'string'
43859                         },
43860                         {
43861                             'name' : 'areaCodes',
43862                             'type' : 'string'
43863                         }
43864                     ]
43865                 })
43866             });
43867             
43868             if(!this.preferedCountries) {
43869                 this.preferedCountries = [
43870                     'hk',
43871                     'gb',
43872                     'us'
43873                 ];
43874             }
43875             
43876             var p = this.preferedCountries.reverse();
43877             
43878             if(p) {
43879                 for (var i = 0; i < p.length; i++) {
43880                     for (var j = 0; j < this.allCountries.length; j++) {
43881                         if(this.allCountries[j].iso2 == p[i]) {
43882                             var t = this.allCountries[j];
43883                             this.allCountries.splice(j,1);
43884                             this.allCountries.unshift(t);
43885                         }
43886                     } 
43887                 }
43888             }
43889             
43890             this.store.proxy.data = {
43891                 success: true,
43892                 data: this.allCountries
43893             };
43894             
43895             return cfg;
43896         },
43897         
43898         initEvents : function()
43899         {
43900             this.createList();
43901             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43902             
43903             this.indicator = this.indicatorEl();
43904             this.flag = this.flagEl();
43905             this.dialCodeHolder = this.dialCodeHolderEl();
43906             
43907             this.trigger = this.el.select('div.flag-box',true).first();
43908             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43909             
43910             var _this = this;
43911             
43912             (function(){
43913                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43914                 _this.list.setWidth(lw);
43915             }).defer(100);
43916             
43917             this.list.on('mouseover', this.onViewOver, this);
43918             this.list.on('mousemove', this.onViewMove, this);
43919             this.inputEl().on("keyup", this.onKeyUp, this);
43920             this.inputEl().on("keypress", this.onKeyPress, this);
43921             
43922             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43923
43924             this.view = new Roo.View(this.list, this.tpl, {
43925                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43926             });
43927             
43928             this.view.on('click', this.onViewClick, this);
43929             this.setValue(this.defaultDialCode);
43930         },
43931         
43932         onTriggerClick : function(e)
43933         {
43934             Roo.log('trigger click');
43935             if(this.disabled){
43936                 return;
43937             }
43938             
43939             if(this.isExpanded()){
43940                 this.collapse();
43941                 this.hasFocus = false;
43942             }else {
43943                 this.store.load({});
43944                 this.hasFocus = true;
43945                 this.expand();
43946             }
43947         },
43948         
43949         isExpanded : function()
43950         {
43951             return this.list.isVisible();
43952         },
43953         
43954         collapse : function()
43955         {
43956             if(!this.isExpanded()){
43957                 return;
43958             }
43959             this.list.hide();
43960             Roo.get(document).un('mousedown', this.collapseIf, this);
43961             Roo.get(document).un('mousewheel', this.collapseIf, this);
43962             this.fireEvent('collapse', this);
43963             this.validate();
43964         },
43965         
43966         expand : function()
43967         {
43968             Roo.log('expand');
43969
43970             if(this.isExpanded() || !this.hasFocus){
43971                 return;
43972             }
43973             
43974             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43975             this.list.setWidth(lw);
43976             
43977             this.list.show();
43978             this.restrictHeight();
43979             
43980             Roo.get(document).on('mousedown', this.collapseIf, this);
43981             Roo.get(document).on('mousewheel', this.collapseIf, this);
43982             
43983             this.fireEvent('expand', this);
43984         },
43985         
43986         restrictHeight : function()
43987         {
43988             this.list.alignTo(this.inputEl(), this.listAlign);
43989             this.list.alignTo(this.inputEl(), this.listAlign);
43990         },
43991         
43992         onViewOver : function(e, t)
43993         {
43994             if(this.inKeyMode){
43995                 return;
43996             }
43997             var item = this.view.findItemFromChild(t);
43998             
43999             if(item){
44000                 var index = this.view.indexOf(item);
44001                 this.select(index, false);
44002             }
44003         },
44004
44005         // private
44006         onViewClick : function(view, doFocus, el, e)
44007         {
44008             var index = this.view.getSelectedIndexes()[0];
44009             
44010             var r = this.store.getAt(index);
44011             
44012             if(r){
44013                 this.onSelect(r, index);
44014             }
44015             if(doFocus !== false && !this.blockFocus){
44016                 this.inputEl().focus();
44017             }
44018         },
44019         
44020         onViewMove : function(e, t)
44021         {
44022             this.inKeyMode = false;
44023         },
44024         
44025         select : function(index, scrollIntoView)
44026         {
44027             this.selectedIndex = index;
44028             this.view.select(index);
44029             if(scrollIntoView !== false){
44030                 var el = this.view.getNode(index);
44031                 if(el){
44032                     this.list.scrollChildIntoView(el, false);
44033                 }
44034             }
44035         },
44036         
44037         createList : function()
44038         {
44039             this.list = Roo.get(document.body).createChild({
44040                 tag: 'ul',
44041                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44042                 style: 'display:none'
44043             });
44044             
44045             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44046         },
44047         
44048         collapseIf : function(e)
44049         {
44050             var in_combo  = e.within(this.el);
44051             var in_list =  e.within(this.list);
44052             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44053             
44054             if (in_combo || in_list || is_list) {
44055                 return;
44056             }
44057             this.collapse();
44058         },
44059         
44060         onSelect : function(record, index)
44061         {
44062             if(this.fireEvent('beforeselect', this, record, index) !== false){
44063                 
44064                 this.setFlagClass(record.data.iso2);
44065                 this.setDialCode(record.data.dialCode);
44066                 this.hasFocus = false;
44067                 this.collapse();
44068                 this.fireEvent('select', this, record, index);
44069             }
44070         },
44071         
44072         flagEl : function()
44073         {
44074             var flag = this.el.select('div.flag',true).first();
44075             if(!flag){
44076                 return false;
44077             }
44078             return flag;
44079         },
44080         
44081         dialCodeHolderEl : function()
44082         {
44083             var d = this.el.select('input.dial-code-holder',true).first();
44084             if(!d){
44085                 return false;
44086             }
44087             return d;
44088         },
44089         
44090         setDialCode : function(v)
44091         {
44092             this.dialCodeHolder.dom.value = '+'+v;
44093         },
44094         
44095         setFlagClass : function(n)
44096         {
44097             this.flag.dom.className = 'flag '+n;
44098         },
44099         
44100         getValue : function()
44101         {
44102             var v = this.inputEl().getValue();
44103             if(this.dialCodeHolder) {
44104                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44105             }
44106             return v;
44107         },
44108         
44109         setValue : function(v)
44110         {
44111             var d = this.getDialCode(v);
44112             
44113             //invalid dial code
44114             if(v.length == 0 || !d || d.length == 0) {
44115                 if(this.rendered){
44116                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44117                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44118                 }
44119                 return;
44120             }
44121             
44122             //valid dial code
44123             this.setFlagClass(this.dialCodeMapping[d].iso2);
44124             this.setDialCode(d);
44125             this.inputEl().dom.value = v.replace('+'+d,'');
44126             this.hiddenEl().dom.value = this.getValue();
44127             
44128             this.validate();
44129         },
44130         
44131         getDialCode : function(v)
44132         {
44133             v = v ||  '';
44134             
44135             if (v.length == 0) {
44136                 return this.dialCodeHolder.dom.value;
44137             }
44138             
44139             var dialCode = "";
44140             if (v.charAt(0) != "+") {
44141                 return false;
44142             }
44143             var numericChars = "";
44144             for (var i = 1; i < v.length; i++) {
44145               var c = v.charAt(i);
44146               if (!isNaN(c)) {
44147                 numericChars += c;
44148                 if (this.dialCodeMapping[numericChars]) {
44149                   dialCode = v.substr(1, i);
44150                 }
44151                 if (numericChars.length == 4) {
44152                   break;
44153                 }
44154               }
44155             }
44156             return dialCode;
44157         },
44158         
44159         reset : function()
44160         {
44161             this.setValue(this.defaultDialCode);
44162             this.validate();
44163         },
44164         
44165         hiddenEl : function()
44166         {
44167             return this.el.select('input.hidden-tel-input',true).first();
44168         },
44169         
44170         // after setting val
44171         onKeyUp : function(e){
44172             this.setValue(this.getValue());
44173         },
44174         
44175         onKeyPress : function(e){
44176             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44177                 e.stopEvent();
44178             }
44179         }
44180         
44181 });
44182 /**
44183  * @class Roo.bootstrap.MoneyField
44184  * @extends Roo.bootstrap.ComboBox
44185  * Bootstrap MoneyField class
44186  * 
44187  * @constructor
44188  * Create a new MoneyField.
44189  * @param {Object} config Configuration options
44190  */
44191
44192 Roo.bootstrap.MoneyField = function(config) {
44193     
44194     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44195     
44196 };
44197
44198 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44199     
44200     /**
44201      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44202      */
44203     allowDecimals : true,
44204     /**
44205      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44206      */
44207     decimalSeparator : ".",
44208     /**
44209      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44210      */
44211     decimalPrecision : 0,
44212     /**
44213      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44214      */
44215     allowNegative : true,
44216     /**
44217      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44218      */
44219     allowZero: true,
44220     /**
44221      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44222      */
44223     minValue : Number.NEGATIVE_INFINITY,
44224     /**
44225      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44226      */
44227     maxValue : Number.MAX_VALUE,
44228     /**
44229      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44230      */
44231     minText : "The minimum value for this field is {0}",
44232     /**
44233      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44234      */
44235     maxText : "The maximum value for this field is {0}",
44236     /**
44237      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44238      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44239      */
44240     nanText : "{0} is not a valid number",
44241     /**
44242      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44243      */
44244     castInt : true,
44245     /**
44246      * @cfg {String} defaults currency of the MoneyField
44247      * value should be in lkey
44248      */
44249     defaultCurrency : false,
44250     /**
44251      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44252      */
44253     thousandsDelimiter : false,
44254     /**
44255      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44256      */
44257     max_length: false,
44258     
44259     inputlg : 9,
44260     inputmd : 9,
44261     inputsm : 9,
44262     inputxs : 6,
44263     
44264     store : false,
44265     
44266     getAutoCreate : function()
44267     {
44268         var align = this.labelAlign || this.parentLabelAlign();
44269         
44270         var id = Roo.id();
44271
44272         var cfg = {
44273             cls: 'form-group',
44274             cn: []
44275         };
44276
44277         var input =  {
44278             tag: 'input',
44279             id : id,
44280             cls : 'form-control roo-money-amount-input',
44281             autocomplete: 'new-password'
44282         };
44283         
44284         var hiddenInput = {
44285             tag: 'input',
44286             type: 'hidden',
44287             id: Roo.id(),
44288             cls: 'hidden-number-input'
44289         };
44290         
44291         if(this.max_length) {
44292             input.maxlength = this.max_length; 
44293         }
44294         
44295         if (this.name) {
44296             hiddenInput.name = this.name;
44297         }
44298
44299         if (this.disabled) {
44300             input.disabled = true;
44301         }
44302
44303         var clg = 12 - this.inputlg;
44304         var cmd = 12 - this.inputmd;
44305         var csm = 12 - this.inputsm;
44306         var cxs = 12 - this.inputxs;
44307         
44308         var container = {
44309             tag : 'div',
44310             cls : 'row roo-money-field',
44311             cn : [
44312                 {
44313                     tag : 'div',
44314                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44315                     cn : [
44316                         {
44317                             tag : 'div',
44318                             cls: 'roo-select2-container input-group',
44319                             cn: [
44320                                 {
44321                                     tag : 'input',
44322                                     cls : 'form-control roo-money-currency-input',
44323                                     autocomplete: 'new-password',
44324                                     readOnly : 1,
44325                                     name : this.currencyName
44326                                 },
44327                                 {
44328                                     tag :'span',
44329                                     cls : 'input-group-addon',
44330                                     cn : [
44331                                         {
44332                                             tag: 'span',
44333                                             cls: 'caret'
44334                                         }
44335                                     ]
44336                                 }
44337                             ]
44338                         }
44339                     ]
44340                 },
44341                 {
44342                     tag : 'div',
44343                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44344                     cn : [
44345                         {
44346                             tag: 'div',
44347                             cls: this.hasFeedback ? 'has-feedback' : '',
44348                             cn: [
44349                                 input
44350                             ]
44351                         }
44352                     ]
44353                 }
44354             ]
44355             
44356         };
44357         
44358         if (this.fieldLabel.length) {
44359             var indicator = {
44360                 tag: 'i',
44361                 tooltip: 'This field is required'
44362             };
44363
44364             var label = {
44365                 tag: 'label',
44366                 'for':  id,
44367                 cls: 'control-label',
44368                 cn: []
44369             };
44370
44371             var label_text = {
44372                 tag: 'span',
44373                 html: this.fieldLabel
44374             };
44375
44376             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44377             label.cn = [
44378                 indicator,
44379                 label_text
44380             ];
44381
44382             if(this.indicatorpos == 'right') {
44383                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44384                 label.cn = [
44385                     label_text,
44386                     indicator
44387                 ];
44388             }
44389
44390             if(align == 'left') {
44391                 container = {
44392                     tag: 'div',
44393                     cn: [
44394                         container
44395                     ]
44396                 };
44397
44398                 if(this.labelWidth > 12){
44399                     label.style = "width: " + this.labelWidth + 'px';
44400                 }
44401                 if(this.labelWidth < 13 && this.labelmd == 0){
44402                     this.labelmd = this.labelWidth;
44403                 }
44404                 if(this.labellg > 0){
44405                     label.cls += ' col-lg-' + this.labellg;
44406                     input.cls += ' col-lg-' + (12 - this.labellg);
44407                 }
44408                 if(this.labelmd > 0){
44409                     label.cls += ' col-md-' + this.labelmd;
44410                     container.cls += ' col-md-' + (12 - this.labelmd);
44411                 }
44412                 if(this.labelsm > 0){
44413                     label.cls += ' col-sm-' + this.labelsm;
44414                     container.cls += ' col-sm-' + (12 - this.labelsm);
44415                 }
44416                 if(this.labelxs > 0){
44417                     label.cls += ' col-xs-' + this.labelxs;
44418                     container.cls += ' col-xs-' + (12 - this.labelxs);
44419                 }
44420             }
44421         }
44422
44423         cfg.cn = [
44424             label,
44425             container,
44426             hiddenInput
44427         ];
44428         
44429         var settings = this;
44430
44431         ['xs','sm','md','lg'].map(function(size){
44432             if (settings[size]) {
44433                 cfg.cls += ' col-' + size + '-' + settings[size];
44434             }
44435         });
44436         
44437         return cfg;
44438     },
44439     
44440     initEvents : function()
44441     {
44442         this.indicator = this.indicatorEl();
44443         
44444         this.initCurrencyEvent();
44445         
44446         this.initNumberEvent();
44447     },
44448     
44449     initCurrencyEvent : function()
44450     {
44451         if (!this.store) {
44452             throw "can not find store for combo";
44453         }
44454         
44455         this.store = Roo.factory(this.store, Roo.data);
44456         this.store.parent = this;
44457         
44458         this.createList();
44459         
44460         this.triggerEl = this.el.select('.input-group-addon', true).first();
44461         
44462         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44463         
44464         var _this = this;
44465         
44466         (function(){
44467             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44468             _this.list.setWidth(lw);
44469         }).defer(100);
44470         
44471         this.list.on('mouseover', this.onViewOver, this);
44472         this.list.on('mousemove', this.onViewMove, this);
44473         this.list.on('scroll', this.onViewScroll, this);
44474         
44475         if(!this.tpl){
44476             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44477         }
44478         
44479         this.view = new Roo.View(this.list, this.tpl, {
44480             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44481         });
44482         
44483         this.view.on('click', this.onViewClick, this);
44484         
44485         this.store.on('beforeload', this.onBeforeLoad, this);
44486         this.store.on('load', this.onLoad, this);
44487         this.store.on('loadexception', this.onLoadException, this);
44488         
44489         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44490             "up" : function(e){
44491                 this.inKeyMode = true;
44492                 this.selectPrev();
44493             },
44494
44495             "down" : function(e){
44496                 if(!this.isExpanded()){
44497                     this.onTriggerClick();
44498                 }else{
44499                     this.inKeyMode = true;
44500                     this.selectNext();
44501                 }
44502             },
44503
44504             "enter" : function(e){
44505                 this.collapse();
44506                 
44507                 if(this.fireEvent("specialkey", this, e)){
44508                     this.onViewClick(false);
44509                 }
44510                 
44511                 return true;
44512             },
44513
44514             "esc" : function(e){
44515                 this.collapse();
44516             },
44517
44518             "tab" : function(e){
44519                 this.collapse();
44520                 
44521                 if(this.fireEvent("specialkey", this, e)){
44522                     this.onViewClick(false);
44523                 }
44524                 
44525                 return true;
44526             },
44527
44528             scope : this,
44529
44530             doRelay : function(foo, bar, hname){
44531                 if(hname == 'down' || this.scope.isExpanded()){
44532                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44533                 }
44534                 return true;
44535             },
44536
44537             forceKeyDown: true
44538         });
44539         
44540         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44541         
44542     },
44543     
44544     initNumberEvent : function(e)
44545     {
44546         this.inputEl().on("keydown" , this.fireKey,  this);
44547         this.inputEl().on("focus", this.onFocus,  this);
44548         this.inputEl().on("blur", this.onBlur,  this);
44549         
44550         this.inputEl().relayEvent('keyup', this);
44551         
44552         if(this.indicator){
44553             this.indicator.addClass('invisible');
44554         }
44555  
44556         this.originalValue = this.getValue();
44557         
44558         if(this.validationEvent == 'keyup'){
44559             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44560             this.inputEl().on('keyup', this.filterValidation, this);
44561         }
44562         else if(this.validationEvent !== false){
44563             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44564         }
44565         
44566         if(this.selectOnFocus){
44567             this.on("focus", this.preFocus, this);
44568             
44569         }
44570         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44571             this.inputEl().on("keypress", this.filterKeys, this);
44572         } else {
44573             this.inputEl().relayEvent('keypress', this);
44574         }
44575         
44576         var allowed = "0123456789";
44577         
44578         if(this.allowDecimals){
44579             allowed += this.decimalSeparator;
44580         }
44581         
44582         if(this.allowNegative){
44583             allowed += "-";
44584         }
44585         
44586         if(this.thousandsDelimiter) {
44587             allowed += ",";
44588         }
44589         
44590         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44591         
44592         var keyPress = function(e){
44593             
44594             var k = e.getKey();
44595             
44596             var c = e.getCharCode();
44597             
44598             if(
44599                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44600                     allowed.indexOf(String.fromCharCode(c)) === -1
44601             ){
44602                 e.stopEvent();
44603                 return;
44604             }
44605             
44606             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44607                 return;
44608             }
44609             
44610             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44611                 e.stopEvent();
44612             }
44613         };
44614         
44615         this.inputEl().on("keypress", keyPress, this);
44616         
44617     },
44618     
44619     onTriggerClick : function(e)
44620     {   
44621         if(this.disabled){
44622             return;
44623         }
44624         
44625         this.page = 0;
44626         this.loadNext = false;
44627         
44628         if(this.isExpanded()){
44629             this.collapse();
44630             return;
44631         }
44632         
44633         this.hasFocus = true;
44634         
44635         if(this.triggerAction == 'all') {
44636             this.doQuery(this.allQuery, true);
44637             return;
44638         }
44639         
44640         this.doQuery(this.getRawValue());
44641     },
44642     
44643     getCurrency : function()
44644     {   
44645         var v = this.currencyEl().getValue();
44646         
44647         return v;
44648     },
44649     
44650     restrictHeight : function()
44651     {
44652         this.list.alignTo(this.currencyEl(), this.listAlign);
44653         this.list.alignTo(this.currencyEl(), this.listAlign);
44654     },
44655     
44656     onViewClick : function(view, doFocus, el, e)
44657     {
44658         var index = this.view.getSelectedIndexes()[0];
44659         
44660         var r = this.store.getAt(index);
44661         
44662         if(r){
44663             this.onSelect(r, index);
44664         }
44665     },
44666     
44667     onSelect : function(record, index){
44668         
44669         if(this.fireEvent('beforeselect', this, record, index) !== false){
44670         
44671             this.setFromCurrencyData(index > -1 ? record.data : false);
44672             
44673             this.collapse();
44674             
44675             this.fireEvent('select', this, record, index);
44676         }
44677     },
44678     
44679     setFromCurrencyData : function(o)
44680     {
44681         var currency = '';
44682         
44683         this.lastCurrency = o;
44684         
44685         if (this.currencyField) {
44686             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44687         } else {
44688             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44689         }
44690         
44691         this.lastSelectionText = currency;
44692         
44693         //setting default currency
44694         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44695             this.setCurrency(this.defaultCurrency);
44696             return;
44697         }
44698         
44699         this.setCurrency(currency);
44700     },
44701     
44702     setFromData : function(o)
44703     {
44704         var c = {};
44705         
44706         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44707         
44708         this.setFromCurrencyData(c);
44709         
44710         var value = '';
44711         
44712         if (this.name) {
44713             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44714         } else {
44715             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44716         }
44717         
44718         this.setValue(value);
44719         
44720     },
44721     
44722     setCurrency : function(v)
44723     {   
44724         this.currencyValue = v;
44725         
44726         if(this.rendered){
44727             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44728             this.validate();
44729         }
44730     },
44731     
44732     setValue : function(v)
44733     {
44734         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44735         
44736         this.value = v;
44737         
44738         if(this.rendered){
44739             
44740             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44741             
44742             this.inputEl().dom.value = (v == '') ? '' :
44743                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44744             
44745             if(!this.allowZero && v === '0') {
44746                 this.hiddenEl().dom.value = '';
44747                 this.inputEl().dom.value = '';
44748             }
44749             
44750             this.validate();
44751         }
44752     },
44753     
44754     getRawValue : function()
44755     {
44756         var v = this.inputEl().getValue();
44757         
44758         return v;
44759     },
44760     
44761     getValue : function()
44762     {
44763         return this.fixPrecision(this.parseValue(this.getRawValue()));
44764     },
44765     
44766     parseValue : function(value)
44767     {
44768         if(this.thousandsDelimiter) {
44769             value += "";
44770             r = new RegExp(",", "g");
44771             value = value.replace(r, "");
44772         }
44773         
44774         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44775         return isNaN(value) ? '' : value;
44776         
44777     },
44778     
44779     fixPrecision : function(value)
44780     {
44781         if(this.thousandsDelimiter) {
44782             value += "";
44783             r = new RegExp(",", "g");
44784             value = value.replace(r, "");
44785         }
44786         
44787         var nan = isNaN(value);
44788         
44789         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44790             return nan ? '' : value;
44791         }
44792         return parseFloat(value).toFixed(this.decimalPrecision);
44793     },
44794     
44795     decimalPrecisionFcn : function(v)
44796     {
44797         return Math.floor(v);
44798     },
44799     
44800     validateValue : function(value)
44801     {
44802         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44803             return false;
44804         }
44805         
44806         var num = this.parseValue(value);
44807         
44808         if(isNaN(num)){
44809             this.markInvalid(String.format(this.nanText, value));
44810             return false;
44811         }
44812         
44813         if(num < this.minValue){
44814             this.markInvalid(String.format(this.minText, this.minValue));
44815             return false;
44816         }
44817         
44818         if(num > this.maxValue){
44819             this.markInvalid(String.format(this.maxText, this.maxValue));
44820             return false;
44821         }
44822         
44823         return true;
44824     },
44825     
44826     validate : function()
44827     {
44828         if(this.disabled || this.allowBlank){
44829             this.markValid();
44830             return true;
44831         }
44832         
44833         var currency = this.getCurrency();
44834         
44835         if(this.validateValue(this.getRawValue()) && currency.length){
44836             this.markValid();
44837             return true;
44838         }
44839         
44840         this.markInvalid();
44841         return false;
44842     },
44843     
44844     getName: function()
44845     {
44846         return this.name;
44847     },
44848     
44849     beforeBlur : function()
44850     {
44851         if(!this.castInt){
44852             return;
44853         }
44854         
44855         var v = this.parseValue(this.getRawValue());
44856         
44857         if(v || v == 0){
44858             this.setValue(v);
44859         }
44860     },
44861     
44862     onBlur : function()
44863     {
44864         this.beforeBlur();
44865         
44866         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44867             //this.el.removeClass(this.focusClass);
44868         }
44869         
44870         this.hasFocus = false;
44871         
44872         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44873             this.validate();
44874         }
44875         
44876         var v = this.getValue();
44877         
44878         if(String(v) !== String(this.startValue)){
44879             this.fireEvent('change', this, v, this.startValue);
44880         }
44881         
44882         this.fireEvent("blur", this);
44883     },
44884     
44885     inputEl : function()
44886     {
44887         return this.el.select('.roo-money-amount-input', true).first();
44888     },
44889     
44890     currencyEl : function()
44891     {
44892         return this.el.select('.roo-money-currency-input', true).first();
44893     },
44894     
44895     hiddenEl : function()
44896     {
44897         return this.el.select('input.hidden-number-input',true).first();
44898     }
44899     
44900 });/**
44901  * @class Roo.bootstrap.BezierSignature
44902  * @extends Roo.bootstrap.Component
44903  * Bootstrap BezierSignature class
44904  * This script refer to:
44905  *    Title: Signature Pad
44906  *    Author: szimek
44907  *    Availability: https://github.com/szimek/signature_pad
44908  *
44909  * @constructor
44910  * Create a new BezierSignature
44911  * @param {Object} config The config object
44912  */
44913
44914 Roo.bootstrap.BezierSignature = function(config){
44915     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44916     this.addEvents({
44917         "resize" : true
44918     });
44919 };
44920
44921 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44922 {
44923      
44924     curve_data: [],
44925     
44926     is_empty: true,
44927     
44928     mouse_btn_down: true,
44929     
44930     /**
44931      * @cfg {int} canvas height
44932      */
44933     canvas_height: '200px',
44934     
44935     /**
44936      * @cfg {float|function} Radius of a single dot.
44937      */ 
44938     dot_size: false,
44939     
44940     /**
44941      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44942      */
44943     min_width: 0.5,
44944     
44945     /**
44946      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44947      */
44948     max_width: 2.5,
44949     
44950     /**
44951      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44952      */
44953     throttle: 16,
44954     
44955     /**
44956      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44957      */
44958     min_distance: 5,
44959     
44960     /**
44961      * @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.
44962      */
44963     bg_color: 'rgba(0, 0, 0, 0)',
44964     
44965     /**
44966      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44967      */
44968     dot_color: 'black',
44969     
44970     /**
44971      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44972      */ 
44973     velocity_filter_weight: 0.7,
44974     
44975     /**
44976      * @cfg {function} Callback when stroke begin. 
44977      */
44978     onBegin: false,
44979     
44980     /**
44981      * @cfg {function} Callback when stroke end.
44982      */
44983     onEnd: false,
44984     
44985     getAutoCreate : function()
44986     {
44987         var cls = 'roo-signature column';
44988         
44989         if(this.cls){
44990             cls += ' ' + this.cls;
44991         }
44992         
44993         var col_sizes = [
44994             'lg',
44995             'md',
44996             'sm',
44997             'xs'
44998         ];
44999         
45000         for(var i = 0; i < col_sizes.length; i++) {
45001             if(this[col_sizes[i]]) {
45002                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45003             }
45004         }
45005         
45006         var cfg = {
45007             tag: 'div',
45008             cls: cls,
45009             cn: [
45010                 {
45011                     tag: 'div',
45012                     cls: 'roo-signature-body',
45013                     cn: [
45014                         {
45015                             tag: 'canvas',
45016                             cls: 'roo-signature-body-canvas',
45017                             height: this.canvas_height,
45018                             width: this.canvas_width
45019                         }
45020                     ]
45021                 },
45022                 {
45023                     tag: 'input',
45024                     type: 'file',
45025                     style: 'display: none'
45026                 }
45027             ]
45028         };
45029         
45030         return cfg;
45031     },
45032     
45033     initEvents: function() 
45034     {
45035         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45036         
45037         var canvas = this.canvasEl();
45038         
45039         // mouse && touch event swapping...
45040         canvas.dom.style.touchAction = 'none';
45041         canvas.dom.style.msTouchAction = 'none';
45042         
45043         this.mouse_btn_down = false;
45044         canvas.on('mousedown', this._handleMouseDown, this);
45045         canvas.on('mousemove', this._handleMouseMove, this);
45046         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45047         
45048         if (window.PointerEvent) {
45049             canvas.on('pointerdown', this._handleMouseDown, this);
45050             canvas.on('pointermove', this._handleMouseMove, this);
45051             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45052         }
45053         
45054         if ('ontouchstart' in window) {
45055             canvas.on('touchstart', this._handleTouchStart, this);
45056             canvas.on('touchmove', this._handleTouchMove, this);
45057             canvas.on('touchend', this._handleTouchEnd, this);
45058         }
45059         
45060         Roo.EventManager.onWindowResize(this.resize, this, true);
45061         
45062         // file input event
45063         this.fileEl().on('change', this.uploadImage, this);
45064         
45065         this.clear();
45066         
45067         this.resize();
45068     },
45069     
45070     resize: function(){
45071         
45072         var canvas = this.canvasEl().dom;
45073         var ctx = this.canvasElCtx();
45074         var img_data = false;
45075         
45076         if(canvas.width > 0) {
45077             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45078         }
45079         // setting canvas width will clean img data
45080         canvas.width = 0;
45081         
45082         var style = window.getComputedStyle ? 
45083             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45084             
45085         var padding_left = parseInt(style.paddingLeft) || 0;
45086         var padding_right = parseInt(style.paddingRight) || 0;
45087         
45088         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45089         
45090         if(img_data) {
45091             ctx.putImageData(img_data, 0, 0);
45092         }
45093     },
45094     
45095     _handleMouseDown: function(e)
45096     {
45097         if (e.browserEvent.which === 1) {
45098             this.mouse_btn_down = true;
45099             this.strokeBegin(e);
45100         }
45101     },
45102     
45103     _handleMouseMove: function (e)
45104     {
45105         if (this.mouse_btn_down) {
45106             this.strokeMoveUpdate(e);
45107         }
45108     },
45109     
45110     _handleMouseUp: function (e)
45111     {
45112         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45113             this.mouse_btn_down = false;
45114             this.strokeEnd(e);
45115         }
45116     },
45117     
45118     _handleTouchStart: function (e) {
45119         
45120         e.preventDefault();
45121         if (e.browserEvent.targetTouches.length === 1) {
45122             // var touch = e.browserEvent.changedTouches[0];
45123             // this.strokeBegin(touch);
45124             
45125              this.strokeBegin(e); // assume e catching the correct xy...
45126         }
45127     },
45128     
45129     _handleTouchMove: function (e) {
45130         e.preventDefault();
45131         // var touch = event.targetTouches[0];
45132         // _this._strokeMoveUpdate(touch);
45133         this.strokeMoveUpdate(e);
45134     },
45135     
45136     _handleTouchEnd: function (e) {
45137         var wasCanvasTouched = e.target === this.canvasEl().dom;
45138         if (wasCanvasTouched) {
45139             e.preventDefault();
45140             // var touch = event.changedTouches[0];
45141             // _this._strokeEnd(touch);
45142             this.strokeEnd(e);
45143         }
45144     },
45145     
45146     reset: function () {
45147         this._lastPoints = [];
45148         this._lastVelocity = 0;
45149         this._lastWidth = (this.min_width + this.max_width) / 2;
45150         this.canvasElCtx().fillStyle = this.dot_color;
45151     },
45152     
45153     strokeMoveUpdate: function(e)
45154     {
45155         this.strokeUpdate(e);
45156         
45157         if (this.throttle) {
45158             this.throttleStroke(this.strokeUpdate, this.throttle);
45159         }
45160         else {
45161             this.strokeUpdate(e);
45162         }
45163     },
45164     
45165     strokeBegin: function(e)
45166     {
45167         var newPointGroup = {
45168             color: this.dot_color,
45169             points: []
45170         };
45171         
45172         if (typeof this.onBegin === 'function') {
45173             this.onBegin(e);
45174         }
45175         
45176         this.curve_data.push(newPointGroup);
45177         this.reset();
45178         this.strokeUpdate(e);
45179     },
45180     
45181     strokeUpdate: function(e)
45182     {
45183         var rect = this.canvasEl().dom.getBoundingClientRect();
45184         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45185         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45186         var lastPoints = lastPointGroup.points;
45187         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45188         var isLastPointTooClose = lastPoint
45189             ? point.distanceTo(lastPoint) <= this.min_distance
45190             : false;
45191         var color = lastPointGroup.color;
45192         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45193             var curve = this.addPoint(point);
45194             if (!lastPoint) {
45195                 this.drawDot({color: color, point: point});
45196             }
45197             else if (curve) {
45198                 this.drawCurve({color: color, curve: curve});
45199             }
45200             lastPoints.push({
45201                 time: point.time,
45202                 x: point.x,
45203                 y: point.y
45204             });
45205         }
45206     },
45207     
45208     strokeEnd: function(e)
45209     {
45210         this.strokeUpdate(e);
45211         if (typeof this.onEnd === 'function') {
45212             this.onEnd(e);
45213         }
45214     },
45215     
45216     addPoint:  function (point) {
45217         var _lastPoints = this._lastPoints;
45218         _lastPoints.push(point);
45219         if (_lastPoints.length > 2) {
45220             if (_lastPoints.length === 3) {
45221                 _lastPoints.unshift(_lastPoints[0]);
45222             }
45223             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45224             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45225             _lastPoints.shift();
45226             return curve;
45227         }
45228         return null;
45229     },
45230     
45231     calculateCurveWidths: function (startPoint, endPoint) {
45232         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45233             (1 - this.velocity_filter_weight) * this._lastVelocity;
45234
45235         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45236         var widths = {
45237             end: newWidth,
45238             start: this._lastWidth
45239         };
45240         
45241         this._lastVelocity = velocity;
45242         this._lastWidth = newWidth;
45243         return widths;
45244     },
45245     
45246     drawDot: function (_a) {
45247         var color = _a.color, point = _a.point;
45248         var ctx = this.canvasElCtx();
45249         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45250         ctx.beginPath();
45251         this.drawCurveSegment(point.x, point.y, width);
45252         ctx.closePath();
45253         ctx.fillStyle = color;
45254         ctx.fill();
45255     },
45256     
45257     drawCurve: function (_a) {
45258         var color = _a.color, curve = _a.curve;
45259         var ctx = this.canvasElCtx();
45260         var widthDelta = curve.endWidth - curve.startWidth;
45261         var drawSteps = Math.floor(curve.length()) * 2;
45262         ctx.beginPath();
45263         ctx.fillStyle = color;
45264         for (var i = 0; i < drawSteps; i += 1) {
45265         var t = i / drawSteps;
45266         var tt = t * t;
45267         var ttt = tt * t;
45268         var u = 1 - t;
45269         var uu = u * u;
45270         var uuu = uu * u;
45271         var x = uuu * curve.startPoint.x;
45272         x += 3 * uu * t * curve.control1.x;
45273         x += 3 * u * tt * curve.control2.x;
45274         x += ttt * curve.endPoint.x;
45275         var y = uuu * curve.startPoint.y;
45276         y += 3 * uu * t * curve.control1.y;
45277         y += 3 * u * tt * curve.control2.y;
45278         y += ttt * curve.endPoint.y;
45279         var width = curve.startWidth + ttt * widthDelta;
45280         this.drawCurveSegment(x, y, width);
45281         }
45282         ctx.closePath();
45283         ctx.fill();
45284     },
45285     
45286     drawCurveSegment: function (x, y, width) {
45287         var ctx = this.canvasElCtx();
45288         ctx.moveTo(x, y);
45289         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45290         this.is_empty = false;
45291     },
45292     
45293     clear: function()
45294     {
45295         var ctx = this.canvasElCtx();
45296         var canvas = this.canvasEl().dom;
45297         ctx.fillStyle = this.bg_color;
45298         ctx.clearRect(0, 0, canvas.width, canvas.height);
45299         ctx.fillRect(0, 0, canvas.width, canvas.height);
45300         this.curve_data = [];
45301         this.reset();
45302         this.is_empty = true;
45303     },
45304     
45305     fileEl: function()
45306     {
45307         return  this.el.select('input',true).first();
45308     },
45309     
45310     canvasEl: function()
45311     {
45312         return this.el.select('canvas',true).first();
45313     },
45314     
45315     canvasElCtx: function()
45316     {
45317         return this.el.select('canvas',true).first().dom.getContext('2d');
45318     },
45319     
45320     getImage: function(type)
45321     {
45322         if(this.is_empty) {
45323             return false;
45324         }
45325         
45326         // encryption ?
45327         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45328     },
45329     
45330     drawFromImage: function(img_src)
45331     {
45332         var img = new Image();
45333         
45334         img.onload = function(){
45335             this.canvasElCtx().drawImage(img, 0, 0);
45336         }.bind(this);
45337         
45338         img.src = img_src;
45339         
45340         this.is_empty = false;
45341     },
45342     
45343     selectImage: function()
45344     {
45345         this.fileEl().dom.click();
45346     },
45347     
45348     uploadImage: function(e)
45349     {
45350         var reader = new FileReader();
45351         
45352         reader.onload = function(e){
45353             var img = new Image();
45354             img.onload = function(){
45355                 this.reset();
45356                 this.canvasElCtx().drawImage(img, 0, 0);
45357             }.bind(this);
45358             img.src = e.target.result;
45359         }.bind(this);
45360         
45361         reader.readAsDataURL(e.target.files[0]);
45362     },
45363     
45364     // Bezier Point Constructor
45365     Point: (function () {
45366         function Point(x, y, time) {
45367             this.x = x;
45368             this.y = y;
45369             this.time = time || Date.now();
45370         }
45371         Point.prototype.distanceTo = function (start) {
45372             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45373         };
45374         Point.prototype.equals = function (other) {
45375             return this.x === other.x && this.y === other.y && this.time === other.time;
45376         };
45377         Point.prototype.velocityFrom = function (start) {
45378             return this.time !== start.time
45379             ? this.distanceTo(start) / (this.time - start.time)
45380             : 0;
45381         };
45382         return Point;
45383     }()),
45384     
45385     
45386     // Bezier Constructor
45387     Bezier: (function () {
45388         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45389             this.startPoint = startPoint;
45390             this.control2 = control2;
45391             this.control1 = control1;
45392             this.endPoint = endPoint;
45393             this.startWidth = startWidth;
45394             this.endWidth = endWidth;
45395         }
45396         Bezier.fromPoints = function (points, widths, scope) {
45397             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45398             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45399             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45400         };
45401         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45402             var dx1 = s1.x - s2.x;
45403             var dy1 = s1.y - s2.y;
45404             var dx2 = s2.x - s3.x;
45405             var dy2 = s2.y - s3.y;
45406             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45407             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45408             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45409             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45410             var dxm = m1.x - m2.x;
45411             var dym = m1.y - m2.y;
45412             var k = l2 / (l1 + l2);
45413             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45414             var tx = s2.x - cm.x;
45415             var ty = s2.y - cm.y;
45416             return {
45417                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45418                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45419             };
45420         };
45421         Bezier.prototype.length = function () {
45422             var steps = 10;
45423             var length = 0;
45424             var px;
45425             var py;
45426             for (var i = 0; i <= steps; i += 1) {
45427                 var t = i / steps;
45428                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45429                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45430                 if (i > 0) {
45431                     var xdiff = cx - px;
45432                     var ydiff = cy - py;
45433                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45434                 }
45435                 px = cx;
45436                 py = cy;
45437             }
45438             return length;
45439         };
45440         Bezier.prototype.point = function (t, start, c1, c2, end) {
45441             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45442             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45443             + (3.0 * c2 * (1.0 - t) * t * t)
45444             + (end * t * t * t);
45445         };
45446         return Bezier;
45447     }()),
45448     
45449     throttleStroke: function(fn, wait) {
45450       if (wait === void 0) { wait = 250; }
45451       var previous = 0;
45452       var timeout = null;
45453       var result;
45454       var storedContext;
45455       var storedArgs;
45456       var later = function () {
45457           previous = Date.now();
45458           timeout = null;
45459           result = fn.apply(storedContext, storedArgs);
45460           if (!timeout) {
45461               storedContext = null;
45462               storedArgs = [];
45463           }
45464       };
45465       return function wrapper() {
45466           var args = [];
45467           for (var _i = 0; _i < arguments.length; _i++) {
45468               args[_i] = arguments[_i];
45469           }
45470           var now = Date.now();
45471           var remaining = wait - (now - previous);
45472           storedContext = this;
45473           storedArgs = args;
45474           if (remaining <= 0 || remaining > wait) {
45475               if (timeout) {
45476                   clearTimeout(timeout);
45477                   timeout = null;
45478               }
45479               previous = now;
45480               result = fn.apply(storedContext, storedArgs);
45481               if (!timeout) {
45482                   storedContext = null;
45483                   storedArgs = [];
45484               }
45485           }
45486           else if (!timeout) {
45487               timeout = window.setTimeout(later, remaining);
45488           }
45489           return result;
45490       };
45491   }
45492   
45493 });
45494
45495  
45496
45497