71b4af01b9083d6fe8f1631fc090a1d0338d4320
[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  * @builder-top
21065  * Bootstrap Popover class
21066  * @cfg {String} html contents of the popover   (or false to use children..)
21067  * @cfg {String} title of popover (or false to hide)
21068  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21069  * @cfg {String} trigger click || hover (or false to trigger manually)
21070  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21071  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21072  *      - if false and it has a 'parent' then it will be automatically added to that element
21073  *      - if string - Roo.get  will be called 
21074  * @cfg {Number} delay - delay before showing
21075  
21076  * @constructor
21077  * Create a new Popover
21078  * @param {Object} config The config object
21079  */
21080
21081 Roo.bootstrap.Popover = function(config){
21082     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21083     
21084     this.addEvents({
21085         // raw events
21086          /**
21087          * @event show
21088          * After the popover show
21089          * 
21090          * @param {Roo.bootstrap.Popover} this
21091          */
21092         "show" : true,
21093         /**
21094          * @event hide
21095          * After the popover hide
21096          * 
21097          * @param {Roo.bootstrap.Popover} this
21098          */
21099         "hide" : true
21100     });
21101 };
21102
21103 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21104     
21105     title: false,
21106     html: false,
21107     
21108     placement : 'right',
21109     trigger : 'hover', // hover
21110     modal : false,
21111     delay : 0,
21112     
21113     over: false,
21114     
21115     can_build_overlaid : false,
21116     
21117     maskEl : false, // the mask element
21118     headerEl : false,
21119     contentEl : false,
21120     alignEl : false, // when show is called with an element - this get's stored.
21121     
21122     getChildContainer : function()
21123     {
21124         return this.contentEl;
21125         
21126     },
21127     getPopoverHeader : function()
21128     {
21129         this.title = true; // flag not to hide it..
21130         this.headerEl.addClass('p-0');
21131         return this.headerEl
21132     },
21133     
21134     
21135     getAutoCreate : function(){
21136          
21137         var cfg = {
21138            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21139            style: 'display:block',
21140            cn : [
21141                 {
21142                     cls : 'arrow'
21143                 },
21144                 {
21145                     cls : 'popover-inner ',
21146                     cn : [
21147                         {
21148                             tag: 'h3',
21149                             cls: 'popover-title popover-header',
21150                             html : this.title === false ? '' : this.title
21151                         },
21152                         {
21153                             cls : 'popover-content popover-body '  + (this.cls || ''),
21154                             html : this.html || ''
21155                         }
21156                     ]
21157                     
21158                 }
21159            ]
21160         };
21161         
21162         return cfg;
21163     },
21164     /**
21165      * @param {string} the title
21166      */
21167     setTitle: function(str)
21168     {
21169         this.title = str;
21170         if (this.el) {
21171             this.headerEl.dom.innerHTML = str;
21172         }
21173         
21174     },
21175     /**
21176      * @param {string} the body content
21177      */
21178     setContent: function(str)
21179     {
21180         this.html = str;
21181         if (this.contentEl) {
21182             this.contentEl.dom.innerHTML = str;
21183         }
21184         
21185     },
21186     // as it get's added to the bottom of the page.
21187     onRender : function(ct, position)
21188     {
21189         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21190         
21191         
21192         
21193         if(!this.el){
21194             var cfg = Roo.apply({},  this.getAutoCreate());
21195             cfg.id = Roo.id();
21196             
21197             if (this.cls) {
21198                 cfg.cls += ' ' + this.cls;
21199             }
21200             if (this.style) {
21201                 cfg.style = this.style;
21202             }
21203             //Roo.log("adding to ");
21204             this.el = Roo.get(document.body).createChild(cfg, position);
21205 //            Roo.log(this.el);
21206         }
21207         
21208         this.contentEl = this.el.select('.popover-content',true).first();
21209         this.headerEl =  this.el.select('.popover-title',true).first();
21210         
21211         var nitems = [];
21212         if(typeof(this.items) != 'undefined'){
21213             var items = this.items;
21214             delete this.items;
21215
21216             for(var i =0;i < items.length;i++) {
21217                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21218             }
21219         }
21220
21221         this.items = nitems;
21222         
21223         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21224         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21225         
21226         
21227         
21228         this.initEvents();
21229     },
21230     
21231     resizeMask : function()
21232     {
21233         this.maskEl.setSize(
21234             Roo.lib.Dom.getViewWidth(true),
21235             Roo.lib.Dom.getViewHeight(true)
21236         );
21237     },
21238     
21239     initEvents : function()
21240     {
21241         
21242         if (!this.modal) { 
21243             Roo.bootstrap.Popover.register(this);
21244         }
21245          
21246         this.arrowEl = this.el.select('.arrow',true).first();
21247         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21248         this.el.enableDisplayMode('block');
21249         this.el.hide();
21250  
21251         
21252         if (this.over === false && !this.parent()) {
21253             return; 
21254         }
21255         if (this.triggers === false) {
21256             return;
21257         }
21258          
21259         // support parent
21260         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21261         var triggers = this.trigger ? this.trigger.split(' ') : [];
21262         Roo.each(triggers, function(trigger) {
21263         
21264             if (trigger == 'click') {
21265                 on_el.on('click', this.toggle, this);
21266             } else if (trigger != 'manual') {
21267                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21268                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21269       
21270                 on_el.on(eventIn  ,this.enter, this);
21271                 on_el.on(eventOut, this.leave, this);
21272             }
21273         }, this);
21274     },
21275     
21276     
21277     // private
21278     timeout : null,
21279     hoverState : null,
21280     
21281     toggle : function () {
21282         this.hoverState == 'in' ? this.leave() : this.enter();
21283     },
21284     
21285     enter : function () {
21286         
21287         clearTimeout(this.timeout);
21288     
21289         this.hoverState = 'in';
21290     
21291         if (!this.delay || !this.delay.show) {
21292             this.show();
21293             return;
21294         }
21295         var _t = this;
21296         this.timeout = setTimeout(function () {
21297             if (_t.hoverState == 'in') {
21298                 _t.show();
21299             }
21300         }, this.delay.show)
21301     },
21302     
21303     leave : function() {
21304         clearTimeout(this.timeout);
21305     
21306         this.hoverState = 'out';
21307     
21308         if (!this.delay || !this.delay.hide) {
21309             this.hide();
21310             return;
21311         }
21312         var _t = this;
21313         this.timeout = setTimeout(function () {
21314             if (_t.hoverState == 'out') {
21315                 _t.hide();
21316             }
21317         }, this.delay.hide)
21318     },
21319     /**
21320      * Show the popover
21321      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21322      * @param {string} (left|right|top|bottom) position
21323      */
21324     show : function (on_el, placement)
21325     {
21326         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21327         on_el = on_el || false; // default to false
21328          
21329         if (!on_el) {
21330             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21331                 on_el = this.parent().el;
21332             } else if (this.over) {
21333                 on_el = Roo.get(this.over);
21334             }
21335             
21336         }
21337         
21338         this.alignEl = Roo.get( on_el );
21339
21340         if (!this.el) {
21341             this.render(document.body);
21342         }
21343         
21344         
21345          
21346         
21347         if (this.title === false) {
21348             this.headerEl.hide();
21349         }
21350         
21351        
21352         this.el.show();
21353         this.el.dom.style.display = 'block';
21354          
21355  
21356         if (this.alignEl) {
21357             this.updatePosition(this.placement, true);
21358              
21359         } else {
21360             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21361             var es = this.el.getSize();
21362             var x = Roo.lib.Dom.getViewWidth()/2;
21363             var y = Roo.lib.Dom.getViewHeight()/2;
21364             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21365             
21366         }
21367
21368         
21369         //var arrow = this.el.select('.arrow',true).first();
21370         //arrow.set(align[2], 
21371         
21372         this.el.addClass('in');
21373         
21374          
21375         
21376         this.hoverState = 'in';
21377         
21378         if (this.modal) {
21379             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21380             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21381             this.maskEl.dom.style.display = 'block';
21382             this.maskEl.addClass('show');
21383         }
21384         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21385  
21386         this.fireEvent('show', this);
21387         
21388     },
21389     /**
21390      * fire this manually after loading a grid in the table for example
21391      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21392      * @param {Boolean} try and move it if we cant get right position.
21393      */
21394     updatePosition : function(placement, try_move)
21395     {
21396         // allow for calling with no parameters
21397         placement = placement   ? placement :  this.placement;
21398         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21399         
21400         this.el.removeClass([
21401             'fade','top','bottom', 'left', 'right','in',
21402             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21403         ]);
21404         this.el.addClass(placement + ' bs-popover-' + placement);
21405         
21406         if (!this.alignEl ) {
21407             return false;
21408         }
21409         
21410         switch (placement) {
21411             case 'right':
21412                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21413                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21414                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21415                     //normal display... or moved up/down.
21416                     this.el.setXY(offset);
21417                     var xy = this.alignEl.getAnchorXY('tr', false);
21418                     xy[0]+=2;xy[1]+=5;
21419                     this.arrowEl.setXY(xy);
21420                     return true;
21421                 }
21422                 // continue through...
21423                 return this.updatePosition('left', false);
21424                 
21425             
21426             case 'left':
21427                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21428                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21429                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21430                     //normal display... or moved up/down.
21431                     this.el.setXY(offset);
21432                     var xy = this.alignEl.getAnchorXY('tl', false);
21433                     xy[0]-=10;xy[1]+=5; // << fix me
21434                     this.arrowEl.setXY(xy);
21435                     return true;
21436                 }
21437                 // call self...
21438                 return this.updatePosition('right', false);
21439             
21440             case 'top':
21441                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21442                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21443                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21444                     //normal display... or moved up/down.
21445                     this.el.setXY(offset);
21446                     var xy = this.alignEl.getAnchorXY('t', false);
21447                     xy[1]-=10; // << fix me
21448                     this.arrowEl.setXY(xy);
21449                     return true;
21450                 }
21451                 // fall through
21452                return this.updatePosition('bottom', false);
21453             
21454             case 'bottom':
21455                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21456                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21457                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21458                     //normal display... or moved up/down.
21459                     this.el.setXY(offset);
21460                     var xy = this.alignEl.getAnchorXY('b', false);
21461                      xy[1]+=2; // << fix me
21462                     this.arrowEl.setXY(xy);
21463                     return true;
21464                 }
21465                 // fall through
21466                 return this.updatePosition('top', false);
21467                 
21468             
21469         }
21470         
21471         
21472         return false;
21473     },
21474     
21475     hide : function()
21476     {
21477         this.el.setXY([0,0]);
21478         this.el.removeClass('in');
21479         this.el.hide();
21480         this.hoverState = null;
21481         this.maskEl.hide(); // always..
21482         this.fireEvent('hide', this);
21483     }
21484     
21485 });
21486
21487
21488 Roo.apply(Roo.bootstrap.Popover, {
21489
21490     alignment : {
21491         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21492         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21493         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21494         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21495     },
21496     
21497     zIndex : 20001,
21498
21499     clickHander : false,
21500     
21501     
21502
21503     onMouseDown : function(e)
21504     {
21505         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21506             /// what is nothing is showing..
21507             this.hideAll();
21508         }
21509          
21510     },
21511     
21512     
21513     popups : [],
21514     
21515     register : function(popup)
21516     {
21517         if (!Roo.bootstrap.Popover.clickHandler) {
21518             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21519         }
21520         // hide other popups.
21521         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21522         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21523         this.hideAll(); //<< why?
21524         //this.popups.push(popup);
21525     },
21526     hideAll : function()
21527     {
21528         this.popups.forEach(function(p) {
21529             p.hide();
21530         });
21531     },
21532     onShow : function() {
21533         Roo.bootstrap.Popover.popups.push(this);
21534     },
21535     onHide : function() {
21536         Roo.bootstrap.Popover.popups.remove(this);
21537     } 
21538
21539 });/*
21540  * - LGPL
21541  *
21542  * Card header - holder for the card header elements.
21543  * 
21544  */
21545
21546 /**
21547  * @class Roo.bootstrap.PopoverNav
21548  * @extends Roo.bootstrap.NavGroup
21549  * Bootstrap Popover header navigation class
21550  * @constructor
21551  * Create a new Popover Header Navigation 
21552  * @param {Object} config The config object
21553  */
21554
21555 Roo.bootstrap.PopoverNav = function(config){
21556     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21557 };
21558
21559 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21560     
21561     
21562     container_method : 'getPopoverHeader' 
21563     
21564      
21565     
21566     
21567    
21568 });
21569
21570  
21571
21572  /*
21573  * - LGPL
21574  *
21575  * Progress
21576  * 
21577  */
21578
21579 /**
21580  * @class Roo.bootstrap.Progress
21581  * @extends Roo.bootstrap.Component
21582  * Bootstrap Progress class
21583  * @cfg {Boolean} striped striped of the progress bar
21584  * @cfg {Boolean} active animated of the progress bar
21585  * 
21586  * 
21587  * @constructor
21588  * Create a new Progress
21589  * @param {Object} config The config object
21590  */
21591
21592 Roo.bootstrap.Progress = function(config){
21593     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21594 };
21595
21596 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21597     
21598     striped : false,
21599     active: false,
21600     
21601     getAutoCreate : function(){
21602         var cfg = {
21603             tag: 'div',
21604             cls: 'progress'
21605         };
21606         
21607         
21608         if(this.striped){
21609             cfg.cls += ' progress-striped';
21610         }
21611       
21612         if(this.active){
21613             cfg.cls += ' active';
21614         }
21615         
21616         
21617         return cfg;
21618     }
21619    
21620 });
21621
21622  
21623
21624  /*
21625  * - LGPL
21626  *
21627  * ProgressBar
21628  * 
21629  */
21630
21631 /**
21632  * @class Roo.bootstrap.ProgressBar
21633  * @extends Roo.bootstrap.Component
21634  * Bootstrap ProgressBar class
21635  * @cfg {Number} aria_valuenow aria-value now
21636  * @cfg {Number} aria_valuemin aria-value min
21637  * @cfg {Number} aria_valuemax aria-value max
21638  * @cfg {String} label label for the progress bar
21639  * @cfg {String} panel (success | info | warning | danger )
21640  * @cfg {String} role role of the progress bar
21641  * @cfg {String} sr_only text
21642  * 
21643  * 
21644  * @constructor
21645  * Create a new ProgressBar
21646  * @param {Object} config The config object
21647  */
21648
21649 Roo.bootstrap.ProgressBar = function(config){
21650     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21651 };
21652
21653 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21654     
21655     aria_valuenow : 0,
21656     aria_valuemin : 0,
21657     aria_valuemax : 100,
21658     label : false,
21659     panel : false,
21660     role : false,
21661     sr_only: false,
21662     
21663     getAutoCreate : function()
21664     {
21665         
21666         var cfg = {
21667             tag: 'div',
21668             cls: 'progress-bar',
21669             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21670         };
21671         
21672         if(this.sr_only){
21673             cfg.cn = {
21674                 tag: 'span',
21675                 cls: 'sr-only',
21676                 html: this.sr_only
21677             }
21678         }
21679         
21680         if(this.role){
21681             cfg.role = this.role;
21682         }
21683         
21684         if(this.aria_valuenow){
21685             cfg['aria-valuenow'] = this.aria_valuenow;
21686         }
21687         
21688         if(this.aria_valuemin){
21689             cfg['aria-valuemin'] = this.aria_valuemin;
21690         }
21691         
21692         if(this.aria_valuemax){
21693             cfg['aria-valuemax'] = this.aria_valuemax;
21694         }
21695         
21696         if(this.label && !this.sr_only){
21697             cfg.html = this.label;
21698         }
21699         
21700         if(this.panel){
21701             cfg.cls += ' progress-bar-' + this.panel;
21702         }
21703         
21704         return cfg;
21705     },
21706     
21707     update : function(aria_valuenow)
21708     {
21709         this.aria_valuenow = aria_valuenow;
21710         
21711         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21712     }
21713    
21714 });
21715
21716  
21717
21718  /*
21719  * - LGPL
21720  *
21721  * column
21722  * 
21723  */
21724
21725 /**
21726  * @class Roo.bootstrap.TabGroup
21727  * @extends Roo.bootstrap.Column
21728  * Bootstrap Column class
21729  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21730  * @cfg {Boolean} carousel true to make the group behave like a carousel
21731  * @cfg {Boolean} bullets show bullets for the panels
21732  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21733  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21734  * @cfg {Boolean} showarrow (true|false) show arrow default true
21735  * 
21736  * @constructor
21737  * Create a new TabGroup
21738  * @param {Object} config The config object
21739  */
21740
21741 Roo.bootstrap.TabGroup = function(config){
21742     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21743     if (!this.navId) {
21744         this.navId = Roo.id();
21745     }
21746     this.tabs = [];
21747     Roo.bootstrap.TabGroup.register(this);
21748     
21749 };
21750
21751 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21752     
21753     carousel : false,
21754     transition : false,
21755     bullets : 0,
21756     timer : 0,
21757     autoslide : false,
21758     slideFn : false,
21759     slideOnTouch : false,
21760     showarrow : true,
21761     
21762     getAutoCreate : function()
21763     {
21764         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21765         
21766         cfg.cls += ' tab-content';
21767         
21768         if (this.carousel) {
21769             cfg.cls += ' carousel slide';
21770             
21771             cfg.cn = [{
21772                cls : 'carousel-inner',
21773                cn : []
21774             }];
21775         
21776             if(this.bullets  && !Roo.isTouch){
21777                 
21778                 var bullets = {
21779                     cls : 'carousel-bullets',
21780                     cn : []
21781                 };
21782                
21783                 if(this.bullets_cls){
21784                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21785                 }
21786                 
21787                 bullets.cn.push({
21788                     cls : 'clear'
21789                 });
21790                 
21791                 cfg.cn[0].cn.push(bullets);
21792             }
21793             
21794             if(this.showarrow){
21795                 cfg.cn[0].cn.push({
21796                     tag : 'div',
21797                     class : 'carousel-arrow',
21798                     cn : [
21799                         {
21800                             tag : 'div',
21801                             class : 'carousel-prev',
21802                             cn : [
21803                                 {
21804                                     tag : 'i',
21805                                     class : 'fa fa-chevron-left'
21806                                 }
21807                             ]
21808                         },
21809                         {
21810                             tag : 'div',
21811                             class : 'carousel-next',
21812                             cn : [
21813                                 {
21814                                     tag : 'i',
21815                                     class : 'fa fa-chevron-right'
21816                                 }
21817                             ]
21818                         }
21819                     ]
21820                 });
21821             }
21822             
21823         }
21824         
21825         return cfg;
21826     },
21827     
21828     initEvents:  function()
21829     {
21830 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21831 //            this.el.on("touchstart", this.onTouchStart, this);
21832 //        }
21833         
21834         if(this.autoslide){
21835             var _this = this;
21836             
21837             this.slideFn = window.setInterval(function() {
21838                 _this.showPanelNext();
21839             }, this.timer);
21840         }
21841         
21842         if(this.showarrow){
21843             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21844             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21845         }
21846         
21847         
21848     },
21849     
21850 //    onTouchStart : function(e, el, o)
21851 //    {
21852 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21853 //            return;
21854 //        }
21855 //        
21856 //        this.showPanelNext();
21857 //    },
21858     
21859     
21860     getChildContainer : function()
21861     {
21862         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21863     },
21864     
21865     /**
21866     * register a Navigation item
21867     * @param {Roo.bootstrap.NavItem} the navitem to add
21868     */
21869     register : function(item)
21870     {
21871         this.tabs.push( item);
21872         item.navId = this.navId; // not really needed..
21873         this.addBullet();
21874     
21875     },
21876     
21877     getActivePanel : function()
21878     {
21879         var r = false;
21880         Roo.each(this.tabs, function(t) {
21881             if (t.active) {
21882                 r = t;
21883                 return false;
21884             }
21885             return null;
21886         });
21887         return r;
21888         
21889     },
21890     getPanelByName : function(n)
21891     {
21892         var r = false;
21893         Roo.each(this.tabs, function(t) {
21894             if (t.tabId == n) {
21895                 r = t;
21896                 return false;
21897             }
21898             return null;
21899         });
21900         return r;
21901     },
21902     indexOfPanel : function(p)
21903     {
21904         var r = false;
21905         Roo.each(this.tabs, function(t,i) {
21906             if (t.tabId == p.tabId) {
21907                 r = i;
21908                 return false;
21909             }
21910             return null;
21911         });
21912         return r;
21913     },
21914     /**
21915      * show a specific panel
21916      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21917      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21918      */
21919     showPanel : function (pan)
21920     {
21921         if(this.transition || typeof(pan) == 'undefined'){
21922             Roo.log("waiting for the transitionend");
21923             return false;
21924         }
21925         
21926         if (typeof(pan) == 'number') {
21927             pan = this.tabs[pan];
21928         }
21929         
21930         if (typeof(pan) == 'string') {
21931             pan = this.getPanelByName(pan);
21932         }
21933         
21934         var cur = this.getActivePanel();
21935         
21936         if(!pan || !cur){
21937             Roo.log('pan or acitve pan is undefined');
21938             return false;
21939         }
21940         
21941         if (pan.tabId == this.getActivePanel().tabId) {
21942             return true;
21943         }
21944         
21945         if (false === cur.fireEvent('beforedeactivate')) {
21946             return false;
21947         }
21948         
21949         if(this.bullets > 0 && !Roo.isTouch){
21950             this.setActiveBullet(this.indexOfPanel(pan));
21951         }
21952         
21953         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21954             
21955             //class="carousel-item carousel-item-next carousel-item-left"
21956             
21957             this.transition = true;
21958             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21959             var lr = dir == 'next' ? 'left' : 'right';
21960             pan.el.addClass(dir); // or prev
21961             pan.el.addClass('carousel-item-' + dir); // or prev
21962             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21963             cur.el.addClass(lr); // or right
21964             pan.el.addClass(lr);
21965             cur.el.addClass('carousel-item-' +lr); // or right
21966             pan.el.addClass('carousel-item-' +lr);
21967             
21968             
21969             var _this = this;
21970             cur.el.on('transitionend', function() {
21971                 Roo.log("trans end?");
21972                 
21973                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21974                 pan.setActive(true);
21975                 
21976                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21977                 cur.setActive(false);
21978                 
21979                 _this.transition = false;
21980                 
21981             }, this, { single:  true } );
21982             
21983             return true;
21984         }
21985         
21986         cur.setActive(false);
21987         pan.setActive(true);
21988         
21989         return true;
21990         
21991     },
21992     showPanelNext : function()
21993     {
21994         var i = this.indexOfPanel(this.getActivePanel());
21995         
21996         if (i >= this.tabs.length - 1 && !this.autoslide) {
21997             return;
21998         }
21999         
22000         if (i >= this.tabs.length - 1 && this.autoslide) {
22001             i = -1;
22002         }
22003         
22004         this.showPanel(this.tabs[i+1]);
22005     },
22006     
22007     showPanelPrev : function()
22008     {
22009         var i = this.indexOfPanel(this.getActivePanel());
22010         
22011         if (i  < 1 && !this.autoslide) {
22012             return;
22013         }
22014         
22015         if (i < 1 && this.autoslide) {
22016             i = this.tabs.length;
22017         }
22018         
22019         this.showPanel(this.tabs[i-1]);
22020     },
22021     
22022     
22023     addBullet: function()
22024     {
22025         if(!this.bullets || Roo.isTouch){
22026             return;
22027         }
22028         var ctr = this.el.select('.carousel-bullets',true).first();
22029         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22030         var bullet = ctr.createChild({
22031             cls : 'bullet bullet-' + i
22032         },ctr.dom.lastChild);
22033         
22034         
22035         var _this = this;
22036         
22037         bullet.on('click', (function(e, el, o, ii, t){
22038
22039             e.preventDefault();
22040
22041             this.showPanel(ii);
22042
22043             if(this.autoslide && this.slideFn){
22044                 clearInterval(this.slideFn);
22045                 this.slideFn = window.setInterval(function() {
22046                     _this.showPanelNext();
22047                 }, this.timer);
22048             }
22049
22050         }).createDelegate(this, [i, bullet], true));
22051                 
22052         
22053     },
22054      
22055     setActiveBullet : function(i)
22056     {
22057         if(Roo.isTouch){
22058             return;
22059         }
22060         
22061         Roo.each(this.el.select('.bullet', true).elements, function(el){
22062             el.removeClass('selected');
22063         });
22064
22065         var bullet = this.el.select('.bullet-' + i, true).first();
22066         
22067         if(!bullet){
22068             return;
22069         }
22070         
22071         bullet.addClass('selected');
22072     }
22073     
22074     
22075   
22076 });
22077
22078  
22079
22080  
22081  
22082 Roo.apply(Roo.bootstrap.TabGroup, {
22083     
22084     groups: {},
22085      /**
22086     * register a Navigation Group
22087     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22088     */
22089     register : function(navgrp)
22090     {
22091         this.groups[navgrp.navId] = navgrp;
22092         
22093     },
22094     /**
22095     * fetch a Navigation Group based on the navigation ID
22096     * if one does not exist , it will get created.
22097     * @param {string} the navgroup to add
22098     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22099     */
22100     get: function(navId) {
22101         if (typeof(this.groups[navId]) == 'undefined') {
22102             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22103         }
22104         return this.groups[navId] ;
22105     }
22106     
22107     
22108     
22109 });
22110
22111  /*
22112  * - LGPL
22113  *
22114  * TabPanel
22115  * 
22116  */
22117
22118 /**
22119  * @class Roo.bootstrap.TabPanel
22120  * @extends Roo.bootstrap.Component
22121  * Bootstrap TabPanel class
22122  * @cfg {Boolean} active panel active
22123  * @cfg {String} html panel content
22124  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22125  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22126  * @cfg {String} href click to link..
22127  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22128  * 
22129  * 
22130  * @constructor
22131  * Create a new TabPanel
22132  * @param {Object} config The config object
22133  */
22134
22135 Roo.bootstrap.TabPanel = function(config){
22136     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22137     this.addEvents({
22138         /**
22139              * @event changed
22140              * Fires when the active status changes
22141              * @param {Roo.bootstrap.TabPanel} this
22142              * @param {Boolean} state the new state
22143             
22144          */
22145         'changed': true,
22146         /**
22147              * @event beforedeactivate
22148              * Fires before a tab is de-activated - can be used to do validation on a form.
22149              * @param {Roo.bootstrap.TabPanel} this
22150              * @return {Boolean} false if there is an error
22151             
22152          */
22153         'beforedeactivate': true
22154      });
22155     
22156     this.tabId = this.tabId || Roo.id();
22157   
22158 };
22159
22160 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22161     
22162     active: false,
22163     html: false,
22164     tabId: false,
22165     navId : false,
22166     href : '',
22167     touchSlide : false,
22168     getAutoCreate : function(){
22169         
22170         
22171         var cfg = {
22172             tag: 'div',
22173             // item is needed for carousel - not sure if it has any effect otherwise
22174             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22175             html: this.html || ''
22176         };
22177         
22178         if(this.active){
22179             cfg.cls += ' active';
22180         }
22181         
22182         if(this.tabId){
22183             cfg.tabId = this.tabId;
22184         }
22185         
22186         
22187         
22188         return cfg;
22189     },
22190     
22191     initEvents:  function()
22192     {
22193         var p = this.parent();
22194         
22195         this.navId = this.navId || p.navId;
22196         
22197         if (typeof(this.navId) != 'undefined') {
22198             // not really needed.. but just in case.. parent should be a NavGroup.
22199             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22200             
22201             tg.register(this);
22202             
22203             var i = tg.tabs.length - 1;
22204             
22205             if(this.active && tg.bullets > 0 && i < tg.bullets){
22206                 tg.setActiveBullet(i);
22207             }
22208         }
22209         
22210         this.el.on('click', this.onClick, this);
22211         
22212         if(Roo.isTouch && this.touchSlide){
22213             this.el.on("touchstart", this.onTouchStart, this);
22214             this.el.on("touchmove", this.onTouchMove, this);
22215             this.el.on("touchend", this.onTouchEnd, this);
22216         }
22217         
22218     },
22219     
22220     onRender : function(ct, position)
22221     {
22222         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22223     },
22224     
22225     setActive : function(state)
22226     {
22227         Roo.log("panel - set active " + this.tabId + "=" + state);
22228         
22229         this.active = state;
22230         if (!state) {
22231             this.el.removeClass('active');
22232             
22233         } else  if (!this.el.hasClass('active')) {
22234             this.el.addClass('active');
22235         }
22236         
22237         this.fireEvent('changed', this, state);
22238     },
22239     
22240     onClick : function(e)
22241     {
22242         e.preventDefault();
22243         
22244         if(!this.href.length){
22245             return;
22246         }
22247         
22248         window.location.href = this.href;
22249     },
22250     
22251     startX : 0,
22252     startY : 0,
22253     endX : 0,
22254     endY : 0,
22255     swiping : false,
22256     
22257     onTouchStart : function(e)
22258     {
22259         this.swiping = false;
22260         
22261         this.startX = e.browserEvent.touches[0].clientX;
22262         this.startY = e.browserEvent.touches[0].clientY;
22263     },
22264     
22265     onTouchMove : function(e)
22266     {
22267         this.swiping = true;
22268         
22269         this.endX = e.browserEvent.touches[0].clientX;
22270         this.endY = e.browserEvent.touches[0].clientY;
22271     },
22272     
22273     onTouchEnd : function(e)
22274     {
22275         if(!this.swiping){
22276             this.onClick(e);
22277             return;
22278         }
22279         
22280         var tabGroup = this.parent();
22281         
22282         if(this.endX > this.startX){ // swiping right
22283             tabGroup.showPanelPrev();
22284             return;
22285         }
22286         
22287         if(this.startX > this.endX){ // swiping left
22288             tabGroup.showPanelNext();
22289             return;
22290         }
22291     }
22292     
22293     
22294 });
22295  
22296
22297  
22298
22299  /*
22300  * - LGPL
22301  *
22302  * DateField
22303  * 
22304  */
22305
22306 /**
22307  * @class Roo.bootstrap.DateField
22308  * @extends Roo.bootstrap.Input
22309  * Bootstrap DateField class
22310  * @cfg {Number} weekStart default 0
22311  * @cfg {String} viewMode default empty, (months|years)
22312  * @cfg {String} minViewMode default empty, (months|years)
22313  * @cfg {Number} startDate default -Infinity
22314  * @cfg {Number} endDate default Infinity
22315  * @cfg {Boolean} todayHighlight default false
22316  * @cfg {Boolean} todayBtn default false
22317  * @cfg {Boolean} calendarWeeks default false
22318  * @cfg {Object} daysOfWeekDisabled default empty
22319  * @cfg {Boolean} singleMode default false (true | false)
22320  * 
22321  * @cfg {Boolean} keyboardNavigation default true
22322  * @cfg {String} language default en
22323  * 
22324  * @constructor
22325  * Create a new DateField
22326  * @param {Object} config The config object
22327  */
22328
22329 Roo.bootstrap.DateField = function(config){
22330     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22331      this.addEvents({
22332             /**
22333              * @event show
22334              * Fires when this field show.
22335              * @param {Roo.bootstrap.DateField} this
22336              * @param {Mixed} date The date value
22337              */
22338             show : true,
22339             /**
22340              * @event show
22341              * Fires when this field hide.
22342              * @param {Roo.bootstrap.DateField} this
22343              * @param {Mixed} date The date value
22344              */
22345             hide : true,
22346             /**
22347              * @event select
22348              * Fires when select a date.
22349              * @param {Roo.bootstrap.DateField} this
22350              * @param {Mixed} date The date value
22351              */
22352             select : true,
22353             /**
22354              * @event beforeselect
22355              * Fires when before select a date.
22356              * @param {Roo.bootstrap.DateField} this
22357              * @param {Mixed} date The date value
22358              */
22359             beforeselect : true
22360         });
22361 };
22362
22363 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22364     
22365     /**
22366      * @cfg {String} format
22367      * The default date format string which can be overriden for localization support.  The format must be
22368      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22369      */
22370     format : "m/d/y",
22371     /**
22372      * @cfg {String} altFormats
22373      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22374      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22375      */
22376     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22377     
22378     weekStart : 0,
22379     
22380     viewMode : '',
22381     
22382     minViewMode : '',
22383     
22384     todayHighlight : false,
22385     
22386     todayBtn: false,
22387     
22388     language: 'en',
22389     
22390     keyboardNavigation: true,
22391     
22392     calendarWeeks: false,
22393     
22394     startDate: -Infinity,
22395     
22396     endDate: Infinity,
22397     
22398     daysOfWeekDisabled: [],
22399     
22400     _events: [],
22401     
22402     singleMode : false,
22403     
22404     UTCDate: function()
22405     {
22406         return new Date(Date.UTC.apply(Date, arguments));
22407     },
22408     
22409     UTCToday: function()
22410     {
22411         var today = new Date();
22412         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22413     },
22414     
22415     getDate: function() {
22416             var d = this.getUTCDate();
22417             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22418     },
22419     
22420     getUTCDate: function() {
22421             return this.date;
22422     },
22423     
22424     setDate: function(d) {
22425             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22426     },
22427     
22428     setUTCDate: function(d) {
22429             this.date = d;
22430             this.setValue(this.formatDate(this.date));
22431     },
22432         
22433     onRender: function(ct, position)
22434     {
22435         
22436         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22437         
22438         this.language = this.language || 'en';
22439         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22440         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22441         
22442         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22443         this.format = this.format || 'm/d/y';
22444         this.isInline = false;
22445         this.isInput = true;
22446         this.component = this.el.select('.add-on', true).first() || false;
22447         this.component = (this.component && this.component.length === 0) ? false : this.component;
22448         this.hasInput = this.component && this.inputEl().length;
22449         
22450         if (typeof(this.minViewMode === 'string')) {
22451             switch (this.minViewMode) {
22452                 case 'months':
22453                     this.minViewMode = 1;
22454                     break;
22455                 case 'years':
22456                     this.minViewMode = 2;
22457                     break;
22458                 default:
22459                     this.minViewMode = 0;
22460                     break;
22461             }
22462         }
22463         
22464         if (typeof(this.viewMode === 'string')) {
22465             switch (this.viewMode) {
22466                 case 'months':
22467                     this.viewMode = 1;
22468                     break;
22469                 case 'years':
22470                     this.viewMode = 2;
22471                     break;
22472                 default:
22473                     this.viewMode = 0;
22474                     break;
22475             }
22476         }
22477                 
22478         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22479         
22480 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22481         
22482         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22483         
22484         this.picker().on('mousedown', this.onMousedown, this);
22485         this.picker().on('click', this.onClick, this);
22486         
22487         this.picker().addClass('datepicker-dropdown');
22488         
22489         this.startViewMode = this.viewMode;
22490         
22491         if(this.singleMode){
22492             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22493                 v.setVisibilityMode(Roo.Element.DISPLAY);
22494                 v.hide();
22495             });
22496             
22497             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22498                 v.setStyle('width', '189px');
22499             });
22500         }
22501         
22502         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22503             if(!this.calendarWeeks){
22504                 v.remove();
22505                 return;
22506             }
22507             
22508             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22509             v.attr('colspan', function(i, val){
22510                 return parseInt(val) + 1;
22511             });
22512         });
22513                         
22514         
22515         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22516         
22517         this.setStartDate(this.startDate);
22518         this.setEndDate(this.endDate);
22519         
22520         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22521         
22522         this.fillDow();
22523         this.fillMonths();
22524         this.update();
22525         this.showMode();
22526         
22527         if(this.isInline) {
22528             this.showPopup();
22529         }
22530     },
22531     
22532     picker : function()
22533     {
22534         return this.pickerEl;
22535 //        return this.el.select('.datepicker', true).first();
22536     },
22537     
22538     fillDow: function()
22539     {
22540         var dowCnt = this.weekStart;
22541         
22542         var dow = {
22543             tag: 'tr',
22544             cn: [
22545                 
22546             ]
22547         };
22548         
22549         if(this.calendarWeeks){
22550             dow.cn.push({
22551                 tag: 'th',
22552                 cls: 'cw',
22553                 html: '&nbsp;'
22554             })
22555         }
22556         
22557         while (dowCnt < this.weekStart + 7) {
22558             dow.cn.push({
22559                 tag: 'th',
22560                 cls: 'dow',
22561                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22562             });
22563         }
22564         
22565         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22566     },
22567     
22568     fillMonths: function()
22569     {    
22570         var i = 0;
22571         var months = this.picker().select('>.datepicker-months td', true).first();
22572         
22573         months.dom.innerHTML = '';
22574         
22575         while (i < 12) {
22576             var month = {
22577                 tag: 'span',
22578                 cls: 'month',
22579                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22580             };
22581             
22582             months.createChild(month);
22583         }
22584         
22585     },
22586     
22587     update: function()
22588     {
22589         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;
22590         
22591         if (this.date < this.startDate) {
22592             this.viewDate = new Date(this.startDate);
22593         } else if (this.date > this.endDate) {
22594             this.viewDate = new Date(this.endDate);
22595         } else {
22596             this.viewDate = new Date(this.date);
22597         }
22598         
22599         this.fill();
22600     },
22601     
22602     fill: function() 
22603     {
22604         var d = new Date(this.viewDate),
22605                 year = d.getUTCFullYear(),
22606                 month = d.getUTCMonth(),
22607                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22608                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22609                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22610                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22611                 currentDate = this.date && this.date.valueOf(),
22612                 today = this.UTCToday();
22613         
22614         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22615         
22616 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22617         
22618 //        this.picker.select('>tfoot th.today').
22619 //                                              .text(dates[this.language].today)
22620 //                                              .toggle(this.todayBtn !== false);
22621     
22622         this.updateNavArrows();
22623         this.fillMonths();
22624                                                 
22625         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22626         
22627         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22628          
22629         prevMonth.setUTCDate(day);
22630         
22631         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22632         
22633         var nextMonth = new Date(prevMonth);
22634         
22635         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22636         
22637         nextMonth = nextMonth.valueOf();
22638         
22639         var fillMonths = false;
22640         
22641         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22642         
22643         while(prevMonth.valueOf() <= nextMonth) {
22644             var clsName = '';
22645             
22646             if (prevMonth.getUTCDay() === this.weekStart) {
22647                 if(fillMonths){
22648                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22649                 }
22650                     
22651                 fillMonths = {
22652                     tag: 'tr',
22653                     cn: []
22654                 };
22655                 
22656                 if(this.calendarWeeks){
22657                     // ISO 8601: First week contains first thursday.
22658                     // ISO also states week starts on Monday, but we can be more abstract here.
22659                     var
22660                     // Start of current week: based on weekstart/current date
22661                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22662                     // Thursday of this week
22663                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22664                     // First Thursday of year, year from thursday
22665                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22666                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22667                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22668                     
22669                     fillMonths.cn.push({
22670                         tag: 'td',
22671                         cls: 'cw',
22672                         html: calWeek
22673                     });
22674                 }
22675             }
22676             
22677             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22678                 clsName += ' old';
22679             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22680                 clsName += ' new';
22681             }
22682             if (this.todayHighlight &&
22683                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22684                 prevMonth.getUTCMonth() == today.getMonth() &&
22685                 prevMonth.getUTCDate() == today.getDate()) {
22686                 clsName += ' today';
22687             }
22688             
22689             if (currentDate && prevMonth.valueOf() === currentDate) {
22690                 clsName += ' active';
22691             }
22692             
22693             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22694                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22695                     clsName += ' disabled';
22696             }
22697             
22698             fillMonths.cn.push({
22699                 tag: 'td',
22700                 cls: 'day ' + clsName,
22701                 html: prevMonth.getDate()
22702             });
22703             
22704             prevMonth.setDate(prevMonth.getDate()+1);
22705         }
22706           
22707         var currentYear = this.date && this.date.getUTCFullYear();
22708         var currentMonth = this.date && this.date.getUTCMonth();
22709         
22710         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22711         
22712         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22713             v.removeClass('active');
22714             
22715             if(currentYear === year && k === currentMonth){
22716                 v.addClass('active');
22717             }
22718             
22719             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22720                 v.addClass('disabled');
22721             }
22722             
22723         });
22724         
22725         
22726         year = parseInt(year/10, 10) * 10;
22727         
22728         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22729         
22730         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22731         
22732         year -= 1;
22733         for (var i = -1; i < 11; i++) {
22734             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22735                 tag: 'span',
22736                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22737                 html: year
22738             });
22739             
22740             year += 1;
22741         }
22742     },
22743     
22744     showMode: function(dir) 
22745     {
22746         if (dir) {
22747             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22748         }
22749         
22750         Roo.each(this.picker().select('>div',true).elements, function(v){
22751             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22752             v.hide();
22753         });
22754         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22755     },
22756     
22757     place: function()
22758     {
22759         if(this.isInline) {
22760             return;
22761         }
22762         
22763         this.picker().removeClass(['bottom', 'top']);
22764         
22765         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22766             /*
22767              * place to the top of element!
22768              *
22769              */
22770             
22771             this.picker().addClass('top');
22772             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22773             
22774             return;
22775         }
22776         
22777         this.picker().addClass('bottom');
22778         
22779         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22780     },
22781     
22782     parseDate : function(value)
22783     {
22784         if(!value || value instanceof Date){
22785             return value;
22786         }
22787         var v = Date.parseDate(value, this.format);
22788         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22789             v = Date.parseDate(value, 'Y-m-d');
22790         }
22791         if(!v && this.altFormats){
22792             if(!this.altFormatsArray){
22793                 this.altFormatsArray = this.altFormats.split("|");
22794             }
22795             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22796                 v = Date.parseDate(value, this.altFormatsArray[i]);
22797             }
22798         }
22799         return v;
22800     },
22801     
22802     formatDate : function(date, fmt)
22803     {   
22804         return (!date || !(date instanceof Date)) ?
22805         date : date.dateFormat(fmt || this.format);
22806     },
22807     
22808     onFocus : function()
22809     {
22810         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22811         this.showPopup();
22812     },
22813     
22814     onBlur : function()
22815     {
22816         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22817         
22818         var d = this.inputEl().getValue();
22819         
22820         this.setValue(d);
22821                 
22822         this.hidePopup();
22823     },
22824     
22825     showPopup : function()
22826     {
22827         this.picker().show();
22828         this.update();
22829         this.place();
22830         
22831         this.fireEvent('showpopup', this, this.date);
22832     },
22833     
22834     hidePopup : function()
22835     {
22836         if(this.isInline) {
22837             return;
22838         }
22839         this.picker().hide();
22840         this.viewMode = this.startViewMode;
22841         this.showMode();
22842         
22843         this.fireEvent('hidepopup', this, this.date);
22844         
22845     },
22846     
22847     onMousedown: function(e)
22848     {
22849         e.stopPropagation();
22850         e.preventDefault();
22851     },
22852     
22853     keyup: function(e)
22854     {
22855         Roo.bootstrap.DateField.superclass.keyup.call(this);
22856         this.update();
22857     },
22858
22859     setValue: function(v)
22860     {
22861         if(this.fireEvent('beforeselect', this, v) !== false){
22862             var d = new Date(this.parseDate(v) ).clearTime();
22863         
22864             if(isNaN(d.getTime())){
22865                 this.date = this.viewDate = '';
22866                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22867                 return;
22868             }
22869
22870             v = this.formatDate(d);
22871
22872             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22873
22874             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22875
22876             this.update();
22877
22878             this.fireEvent('select', this, this.date);
22879         }
22880     },
22881     
22882     getValue: function()
22883     {
22884         return this.formatDate(this.date);
22885     },
22886     
22887     fireKey: function(e)
22888     {
22889         if (!this.picker().isVisible()){
22890             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22891                 this.showPopup();
22892             }
22893             return;
22894         }
22895         
22896         var dateChanged = false,
22897         dir, day, month,
22898         newDate, newViewDate;
22899         
22900         switch(e.keyCode){
22901             case 27: // escape
22902                 this.hidePopup();
22903                 e.preventDefault();
22904                 break;
22905             case 37: // left
22906             case 39: // right
22907                 if (!this.keyboardNavigation) {
22908                     break;
22909                 }
22910                 dir = e.keyCode == 37 ? -1 : 1;
22911                 
22912                 if (e.ctrlKey){
22913                     newDate = this.moveYear(this.date, dir);
22914                     newViewDate = this.moveYear(this.viewDate, dir);
22915                 } else if (e.shiftKey){
22916                     newDate = this.moveMonth(this.date, dir);
22917                     newViewDate = this.moveMonth(this.viewDate, dir);
22918                 } else {
22919                     newDate = new Date(this.date);
22920                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22921                     newViewDate = new Date(this.viewDate);
22922                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22923                 }
22924                 if (this.dateWithinRange(newDate)){
22925                     this.date = newDate;
22926                     this.viewDate = newViewDate;
22927                     this.setValue(this.formatDate(this.date));
22928 //                    this.update();
22929                     e.preventDefault();
22930                     dateChanged = true;
22931                 }
22932                 break;
22933             case 38: // up
22934             case 40: // down
22935                 if (!this.keyboardNavigation) {
22936                     break;
22937                 }
22938                 dir = e.keyCode == 38 ? -1 : 1;
22939                 if (e.ctrlKey){
22940                     newDate = this.moveYear(this.date, dir);
22941                     newViewDate = this.moveYear(this.viewDate, dir);
22942                 } else if (e.shiftKey){
22943                     newDate = this.moveMonth(this.date, dir);
22944                     newViewDate = this.moveMonth(this.viewDate, dir);
22945                 } else {
22946                     newDate = new Date(this.date);
22947                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22948                     newViewDate = new Date(this.viewDate);
22949                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22950                 }
22951                 if (this.dateWithinRange(newDate)){
22952                     this.date = newDate;
22953                     this.viewDate = newViewDate;
22954                     this.setValue(this.formatDate(this.date));
22955 //                    this.update();
22956                     e.preventDefault();
22957                     dateChanged = true;
22958                 }
22959                 break;
22960             case 13: // enter
22961                 this.setValue(this.formatDate(this.date));
22962                 this.hidePopup();
22963                 e.preventDefault();
22964                 break;
22965             case 9: // tab
22966                 this.setValue(this.formatDate(this.date));
22967                 this.hidePopup();
22968                 break;
22969             case 16: // shift
22970             case 17: // ctrl
22971             case 18: // alt
22972                 break;
22973             default :
22974                 this.hidePopup();
22975                 
22976         }
22977     },
22978     
22979     
22980     onClick: function(e) 
22981     {
22982         e.stopPropagation();
22983         e.preventDefault();
22984         
22985         var target = e.getTarget();
22986         
22987         if(target.nodeName.toLowerCase() === 'i'){
22988             target = Roo.get(target).dom.parentNode;
22989         }
22990         
22991         var nodeName = target.nodeName;
22992         var className = target.className;
22993         var html = target.innerHTML;
22994         //Roo.log(nodeName);
22995         
22996         switch(nodeName.toLowerCase()) {
22997             case 'th':
22998                 switch(className) {
22999                     case 'switch':
23000                         this.showMode(1);
23001                         break;
23002                     case 'prev':
23003                     case 'next':
23004                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23005                         switch(this.viewMode){
23006                                 case 0:
23007                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23008                                         break;
23009                                 case 1:
23010                                 case 2:
23011                                         this.viewDate = this.moveYear(this.viewDate, dir);
23012                                         break;
23013                         }
23014                         this.fill();
23015                         break;
23016                     case 'today':
23017                         var date = new Date();
23018                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23019 //                        this.fill()
23020                         this.setValue(this.formatDate(this.date));
23021                         
23022                         this.hidePopup();
23023                         break;
23024                 }
23025                 break;
23026             case 'span':
23027                 if (className.indexOf('disabled') < 0) {
23028                 if (!this.viewDate) {
23029                     this.viewDate = new Date();
23030                 }
23031                 this.viewDate.setUTCDate(1);
23032                     if (className.indexOf('month') > -1) {
23033                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23034                     } else {
23035                         var year = parseInt(html, 10) || 0;
23036                         this.viewDate.setUTCFullYear(year);
23037                         
23038                     }
23039                     
23040                     if(this.singleMode){
23041                         this.setValue(this.formatDate(this.viewDate));
23042                         this.hidePopup();
23043                         return;
23044                     }
23045                     
23046                     this.showMode(-1);
23047                     this.fill();
23048                 }
23049                 break;
23050                 
23051             case 'td':
23052                 //Roo.log(className);
23053                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23054                     var day = parseInt(html, 10) || 1;
23055                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23056                         month = (this.viewDate || new Date()).getUTCMonth();
23057
23058                     if (className.indexOf('old') > -1) {
23059                         if(month === 0 ){
23060                             month = 11;
23061                             year -= 1;
23062                         }else{
23063                             month -= 1;
23064                         }
23065                     } else if (className.indexOf('new') > -1) {
23066                         if (month == 11) {
23067                             month = 0;
23068                             year += 1;
23069                         } else {
23070                             month += 1;
23071                         }
23072                     }
23073                     //Roo.log([year,month,day]);
23074                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23075                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23076 //                    this.fill();
23077                     //Roo.log(this.formatDate(this.date));
23078                     this.setValue(this.formatDate(this.date));
23079                     this.hidePopup();
23080                 }
23081                 break;
23082         }
23083     },
23084     
23085     setStartDate: function(startDate)
23086     {
23087         this.startDate = startDate || -Infinity;
23088         if (this.startDate !== -Infinity) {
23089             this.startDate = this.parseDate(this.startDate);
23090         }
23091         this.update();
23092         this.updateNavArrows();
23093     },
23094
23095     setEndDate: function(endDate)
23096     {
23097         this.endDate = endDate || Infinity;
23098         if (this.endDate !== Infinity) {
23099             this.endDate = this.parseDate(this.endDate);
23100         }
23101         this.update();
23102         this.updateNavArrows();
23103     },
23104     
23105     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23106     {
23107         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23108         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23109             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23110         }
23111         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23112             return parseInt(d, 10);
23113         });
23114         this.update();
23115         this.updateNavArrows();
23116     },
23117     
23118     updateNavArrows: function() 
23119     {
23120         if(this.singleMode){
23121             return;
23122         }
23123         
23124         var d = new Date(this.viewDate),
23125         year = d.getUTCFullYear(),
23126         month = d.getUTCMonth();
23127         
23128         Roo.each(this.picker().select('.prev', true).elements, function(v){
23129             v.show();
23130             switch (this.viewMode) {
23131                 case 0:
23132
23133                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23134                         v.hide();
23135                     }
23136                     break;
23137                 case 1:
23138                 case 2:
23139                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23140                         v.hide();
23141                     }
23142                     break;
23143             }
23144         });
23145         
23146         Roo.each(this.picker().select('.next', true).elements, function(v){
23147             v.show();
23148             switch (this.viewMode) {
23149                 case 0:
23150
23151                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23152                         v.hide();
23153                     }
23154                     break;
23155                 case 1:
23156                 case 2:
23157                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23158                         v.hide();
23159                     }
23160                     break;
23161             }
23162         })
23163     },
23164     
23165     moveMonth: function(date, dir)
23166     {
23167         if (!dir) {
23168             return date;
23169         }
23170         var new_date = new Date(date.valueOf()),
23171         day = new_date.getUTCDate(),
23172         month = new_date.getUTCMonth(),
23173         mag = Math.abs(dir),
23174         new_month, test;
23175         dir = dir > 0 ? 1 : -1;
23176         if (mag == 1){
23177             test = dir == -1
23178             // If going back one month, make sure month is not current month
23179             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23180             ? function(){
23181                 return new_date.getUTCMonth() == month;
23182             }
23183             // If going forward one month, make sure month is as expected
23184             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23185             : function(){
23186                 return new_date.getUTCMonth() != new_month;
23187             };
23188             new_month = month + dir;
23189             new_date.setUTCMonth(new_month);
23190             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23191             if (new_month < 0 || new_month > 11) {
23192                 new_month = (new_month + 12) % 12;
23193             }
23194         } else {
23195             // For magnitudes >1, move one month at a time...
23196             for (var i=0; i<mag; i++) {
23197                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23198                 new_date = this.moveMonth(new_date, dir);
23199             }
23200             // ...then reset the day, keeping it in the new month
23201             new_month = new_date.getUTCMonth();
23202             new_date.setUTCDate(day);
23203             test = function(){
23204                 return new_month != new_date.getUTCMonth();
23205             };
23206         }
23207         // Common date-resetting loop -- if date is beyond end of month, make it
23208         // end of month
23209         while (test()){
23210             new_date.setUTCDate(--day);
23211             new_date.setUTCMonth(new_month);
23212         }
23213         return new_date;
23214     },
23215
23216     moveYear: function(date, dir)
23217     {
23218         return this.moveMonth(date, dir*12);
23219     },
23220
23221     dateWithinRange: function(date)
23222     {
23223         return date >= this.startDate && date <= this.endDate;
23224     },
23225
23226     
23227     remove: function() 
23228     {
23229         this.picker().remove();
23230     },
23231     
23232     validateValue : function(value)
23233     {
23234         if(this.getVisibilityEl().hasClass('hidden')){
23235             return true;
23236         }
23237         
23238         if(value.length < 1)  {
23239             if(this.allowBlank){
23240                 return true;
23241             }
23242             return false;
23243         }
23244         
23245         if(value.length < this.minLength){
23246             return false;
23247         }
23248         if(value.length > this.maxLength){
23249             return false;
23250         }
23251         if(this.vtype){
23252             var vt = Roo.form.VTypes;
23253             if(!vt[this.vtype](value, this)){
23254                 return false;
23255             }
23256         }
23257         if(typeof this.validator == "function"){
23258             var msg = this.validator(value);
23259             if(msg !== true){
23260                 return false;
23261             }
23262         }
23263         
23264         if(this.regex && !this.regex.test(value)){
23265             return false;
23266         }
23267         
23268         if(typeof(this.parseDate(value)) == 'undefined'){
23269             return false;
23270         }
23271         
23272         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23273             return false;
23274         }      
23275         
23276         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23277             return false;
23278         } 
23279         
23280         
23281         return true;
23282     },
23283     
23284     reset : function()
23285     {
23286         this.date = this.viewDate = '';
23287         
23288         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23289     }
23290    
23291 });
23292
23293 Roo.apply(Roo.bootstrap.DateField,  {
23294     
23295     head : {
23296         tag: 'thead',
23297         cn: [
23298         {
23299             tag: 'tr',
23300             cn: [
23301             {
23302                 tag: 'th',
23303                 cls: 'prev',
23304                 html: '<i class="fa fa-arrow-left"/>'
23305             },
23306             {
23307                 tag: 'th',
23308                 cls: 'switch',
23309                 colspan: '5'
23310             },
23311             {
23312                 tag: 'th',
23313                 cls: 'next',
23314                 html: '<i class="fa fa-arrow-right"/>'
23315             }
23316
23317             ]
23318         }
23319         ]
23320     },
23321     
23322     content : {
23323         tag: 'tbody',
23324         cn: [
23325         {
23326             tag: 'tr',
23327             cn: [
23328             {
23329                 tag: 'td',
23330                 colspan: '7'
23331             }
23332             ]
23333         }
23334         ]
23335     },
23336     
23337     footer : {
23338         tag: 'tfoot',
23339         cn: [
23340         {
23341             tag: 'tr',
23342             cn: [
23343             {
23344                 tag: 'th',
23345                 colspan: '7',
23346                 cls: 'today'
23347             }
23348                     
23349             ]
23350         }
23351         ]
23352     },
23353     
23354     dates:{
23355         en: {
23356             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23357             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23358             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23359             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23360             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23361             today: "Today"
23362         }
23363     },
23364     
23365     modes: [
23366     {
23367         clsName: 'days',
23368         navFnc: 'Month',
23369         navStep: 1
23370     },
23371     {
23372         clsName: 'months',
23373         navFnc: 'FullYear',
23374         navStep: 1
23375     },
23376     {
23377         clsName: 'years',
23378         navFnc: 'FullYear',
23379         navStep: 10
23380     }]
23381 });
23382
23383 Roo.apply(Roo.bootstrap.DateField,  {
23384   
23385     template : {
23386         tag: 'div',
23387         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23388         cn: [
23389         {
23390             tag: 'div',
23391             cls: 'datepicker-days',
23392             cn: [
23393             {
23394                 tag: 'table',
23395                 cls: 'table-condensed',
23396                 cn:[
23397                 Roo.bootstrap.DateField.head,
23398                 {
23399                     tag: 'tbody'
23400                 },
23401                 Roo.bootstrap.DateField.footer
23402                 ]
23403             }
23404             ]
23405         },
23406         {
23407             tag: 'div',
23408             cls: 'datepicker-months',
23409             cn: [
23410             {
23411                 tag: 'table',
23412                 cls: 'table-condensed',
23413                 cn:[
23414                 Roo.bootstrap.DateField.head,
23415                 Roo.bootstrap.DateField.content,
23416                 Roo.bootstrap.DateField.footer
23417                 ]
23418             }
23419             ]
23420         },
23421         {
23422             tag: 'div',
23423             cls: 'datepicker-years',
23424             cn: [
23425             {
23426                 tag: 'table',
23427                 cls: 'table-condensed',
23428                 cn:[
23429                 Roo.bootstrap.DateField.head,
23430                 Roo.bootstrap.DateField.content,
23431                 Roo.bootstrap.DateField.footer
23432                 ]
23433             }
23434             ]
23435         }
23436         ]
23437     }
23438 });
23439
23440  
23441
23442  /*
23443  * - LGPL
23444  *
23445  * TimeField
23446  * 
23447  */
23448
23449 /**
23450  * @class Roo.bootstrap.TimeField
23451  * @extends Roo.bootstrap.Input
23452  * Bootstrap DateField class
23453  * 
23454  * 
23455  * @constructor
23456  * Create a new TimeField
23457  * @param {Object} config The config object
23458  */
23459
23460 Roo.bootstrap.TimeField = function(config){
23461     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23462     this.addEvents({
23463             /**
23464              * @event show
23465              * Fires when this field show.
23466              * @param {Roo.bootstrap.DateField} thisthis
23467              * @param {Mixed} date The date value
23468              */
23469             show : true,
23470             /**
23471              * @event show
23472              * Fires when this field hide.
23473              * @param {Roo.bootstrap.DateField} this
23474              * @param {Mixed} date The date value
23475              */
23476             hide : true,
23477             /**
23478              * @event select
23479              * Fires when select a date.
23480              * @param {Roo.bootstrap.DateField} this
23481              * @param {Mixed} date The date value
23482              */
23483             select : true
23484         });
23485 };
23486
23487 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23488     
23489     /**
23490      * @cfg {String} format
23491      * The default time format string which can be overriden for localization support.  The format must be
23492      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23493      */
23494     format : "H:i",
23495
23496     getAutoCreate : function()
23497     {
23498         this.after = '<i class="fa far fa-clock"></i>';
23499         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23500         
23501          
23502     },
23503     onRender: function(ct, position)
23504     {
23505         
23506         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23507                 
23508         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23509         
23510         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23511         
23512         this.pop = this.picker().select('>.datepicker-time',true).first();
23513         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23514         
23515         this.picker().on('mousedown', this.onMousedown, this);
23516         this.picker().on('click', this.onClick, this);
23517         
23518         this.picker().addClass('datepicker-dropdown');
23519     
23520         this.fillTime();
23521         this.update();
23522             
23523         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23524         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23525         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23526         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23527         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23528         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23529
23530     },
23531     
23532     fireKey: function(e){
23533         if (!this.picker().isVisible()){
23534             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23535                 this.show();
23536             }
23537             return;
23538         }
23539
23540         e.preventDefault();
23541         
23542         switch(e.keyCode){
23543             case 27: // escape
23544                 this.hide();
23545                 break;
23546             case 37: // left
23547             case 39: // right
23548                 this.onTogglePeriod();
23549                 break;
23550             case 38: // up
23551                 this.onIncrementMinutes();
23552                 break;
23553             case 40: // down
23554                 this.onDecrementMinutes();
23555                 break;
23556             case 13: // enter
23557             case 9: // tab
23558                 this.setTime();
23559                 break;
23560         }
23561     },
23562     
23563     onClick: function(e) {
23564         e.stopPropagation();
23565         e.preventDefault();
23566     },
23567     
23568     picker : function()
23569     {
23570         return this.pickerEl;
23571     },
23572     
23573     fillTime: function()
23574     {    
23575         var time = this.pop.select('tbody', true).first();
23576         
23577         time.dom.innerHTML = '';
23578         
23579         time.createChild({
23580             tag: 'tr',
23581             cn: [
23582                 {
23583                     tag: 'td',
23584                     cn: [
23585                         {
23586                             tag: 'a',
23587                             href: '#',
23588                             cls: 'btn',
23589                             cn: [
23590                                 {
23591                                     tag: 'i',
23592                                     cls: 'hours-up fa fas fa-chevron-up'
23593                                 }
23594                             ]
23595                         } 
23596                     ]
23597                 },
23598                 {
23599                     tag: 'td',
23600                     cls: 'separator'
23601                 },
23602                 {
23603                     tag: 'td',
23604                     cn: [
23605                         {
23606                             tag: 'a',
23607                             href: '#',
23608                             cls: 'btn',
23609                             cn: [
23610                                 {
23611                                     tag: 'i',
23612                                     cls: 'minutes-up fa fas fa-chevron-up'
23613                                 }
23614                             ]
23615                         }
23616                     ]
23617                 },
23618                 {
23619                     tag: 'td',
23620                     cls: 'separator'
23621                 }
23622             ]
23623         });
23624         
23625         time.createChild({
23626             tag: 'tr',
23627             cn: [
23628                 {
23629                     tag: 'td',
23630                     cn: [
23631                         {
23632                             tag: 'span',
23633                             cls: 'timepicker-hour',
23634                             html: '00'
23635                         }  
23636                     ]
23637                 },
23638                 {
23639                     tag: 'td',
23640                     cls: 'separator',
23641                     html: ':'
23642                 },
23643                 {
23644                     tag: 'td',
23645                     cn: [
23646                         {
23647                             tag: 'span',
23648                             cls: 'timepicker-minute',
23649                             html: '00'
23650                         }  
23651                     ]
23652                 },
23653                 {
23654                     tag: 'td',
23655                     cls: 'separator'
23656                 },
23657                 {
23658                     tag: 'td',
23659                     cn: [
23660                         {
23661                             tag: 'button',
23662                             type: 'button',
23663                             cls: 'btn btn-primary period',
23664                             html: 'AM'
23665                             
23666                         }
23667                     ]
23668                 }
23669             ]
23670         });
23671         
23672         time.createChild({
23673             tag: 'tr',
23674             cn: [
23675                 {
23676                     tag: 'td',
23677                     cn: [
23678                         {
23679                             tag: 'a',
23680                             href: '#',
23681                             cls: 'btn',
23682                             cn: [
23683                                 {
23684                                     tag: 'span',
23685                                     cls: 'hours-down fa fas fa-chevron-down'
23686                                 }
23687                             ]
23688                         }
23689                     ]
23690                 },
23691                 {
23692                     tag: 'td',
23693                     cls: 'separator'
23694                 },
23695                 {
23696                     tag: 'td',
23697                     cn: [
23698                         {
23699                             tag: 'a',
23700                             href: '#',
23701                             cls: 'btn',
23702                             cn: [
23703                                 {
23704                                     tag: 'span',
23705                                     cls: 'minutes-down fa fas fa-chevron-down'
23706                                 }
23707                             ]
23708                         }
23709                     ]
23710                 },
23711                 {
23712                     tag: 'td',
23713                     cls: 'separator'
23714                 }
23715             ]
23716         });
23717         
23718     },
23719     
23720     update: function()
23721     {
23722         
23723         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23724         
23725         this.fill();
23726     },
23727     
23728     fill: function() 
23729     {
23730         var hours = this.time.getHours();
23731         var minutes = this.time.getMinutes();
23732         var period = 'AM';
23733         
23734         if(hours > 11){
23735             period = 'PM';
23736         }
23737         
23738         if(hours == 0){
23739             hours = 12;
23740         }
23741         
23742         
23743         if(hours > 12){
23744             hours = hours - 12;
23745         }
23746         
23747         if(hours < 10){
23748             hours = '0' + hours;
23749         }
23750         
23751         if(minutes < 10){
23752             minutes = '0' + minutes;
23753         }
23754         
23755         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23756         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23757         this.pop.select('button', true).first().dom.innerHTML = period;
23758         
23759     },
23760     
23761     place: function()
23762     {   
23763         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23764         
23765         var cls = ['bottom'];
23766         
23767         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23768             cls.pop();
23769             cls.push('top');
23770         }
23771         
23772         cls.push('right');
23773         
23774         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23775             cls.pop();
23776             cls.push('left');
23777         }
23778         //this.picker().setXY(20000,20000);
23779         this.picker().addClass(cls.join('-'));
23780         
23781         var _this = this;
23782         
23783         Roo.each(cls, function(c){
23784             if(c == 'bottom'){
23785                 (function() {
23786                  //  
23787                 }).defer(200);
23788                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23789                 //_this.picker().setTop(_this.inputEl().getHeight());
23790                 return;
23791             }
23792             if(c == 'top'){
23793                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23794                 
23795                 //_this.picker().setTop(0 - _this.picker().getHeight());
23796                 return;
23797             }
23798             /*
23799             if(c == 'left'){
23800                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23801                 return;
23802             }
23803             if(c == 'right'){
23804                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23805                 return;
23806             }
23807             */
23808         });
23809         
23810     },
23811   
23812     onFocus : function()
23813     {
23814         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23815         this.show();
23816     },
23817     
23818     onBlur : function()
23819     {
23820         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23821         this.hide();
23822     },
23823     
23824     show : function()
23825     {
23826         this.picker().show();
23827         this.pop.show();
23828         this.update();
23829         this.place();
23830         
23831         this.fireEvent('show', this, this.date);
23832     },
23833     
23834     hide : function()
23835     {
23836         this.picker().hide();
23837         this.pop.hide();
23838         
23839         this.fireEvent('hide', this, this.date);
23840     },
23841     
23842     setTime : function()
23843     {
23844         this.hide();
23845         this.setValue(this.time.format(this.format));
23846         
23847         this.fireEvent('select', this, this.date);
23848         
23849         
23850     },
23851     
23852     onMousedown: function(e){
23853         e.stopPropagation();
23854         e.preventDefault();
23855     },
23856     
23857     onIncrementHours: function()
23858     {
23859         Roo.log('onIncrementHours');
23860         this.time = this.time.add(Date.HOUR, 1);
23861         this.update();
23862         
23863     },
23864     
23865     onDecrementHours: function()
23866     {
23867         Roo.log('onDecrementHours');
23868         this.time = this.time.add(Date.HOUR, -1);
23869         this.update();
23870     },
23871     
23872     onIncrementMinutes: function()
23873     {
23874         Roo.log('onIncrementMinutes');
23875         this.time = this.time.add(Date.MINUTE, 1);
23876         this.update();
23877     },
23878     
23879     onDecrementMinutes: function()
23880     {
23881         Roo.log('onDecrementMinutes');
23882         this.time = this.time.add(Date.MINUTE, -1);
23883         this.update();
23884     },
23885     
23886     onTogglePeriod: function()
23887     {
23888         Roo.log('onTogglePeriod');
23889         this.time = this.time.add(Date.HOUR, 12);
23890         this.update();
23891     }
23892     
23893    
23894 });
23895  
23896
23897 Roo.apply(Roo.bootstrap.TimeField,  {
23898   
23899     template : {
23900         tag: 'div',
23901         cls: 'datepicker dropdown-menu',
23902         cn: [
23903             {
23904                 tag: 'div',
23905                 cls: 'datepicker-time',
23906                 cn: [
23907                 {
23908                     tag: 'table',
23909                     cls: 'table-condensed',
23910                     cn:[
23911                         {
23912                             tag: 'tbody',
23913                             cn: [
23914                                 {
23915                                     tag: 'tr',
23916                                     cn: [
23917                                     {
23918                                         tag: 'td',
23919                                         colspan: '7'
23920                                     }
23921                                     ]
23922                                 }
23923                             ]
23924                         },
23925                         {
23926                             tag: 'tfoot',
23927                             cn: [
23928                                 {
23929                                     tag: 'tr',
23930                                     cn: [
23931                                     {
23932                                         tag: 'th',
23933                                         colspan: '7',
23934                                         cls: '',
23935                                         cn: [
23936                                             {
23937                                                 tag: 'button',
23938                                                 cls: 'btn btn-info ok',
23939                                                 html: 'OK'
23940                                             }
23941                                         ]
23942                                     }
23943                     
23944                                     ]
23945                                 }
23946                             ]
23947                         }
23948                     ]
23949                 }
23950                 ]
23951             }
23952         ]
23953     }
23954 });
23955
23956  
23957
23958  /*
23959  * - LGPL
23960  *
23961  * MonthField
23962  * 
23963  */
23964
23965 /**
23966  * @class Roo.bootstrap.MonthField
23967  * @extends Roo.bootstrap.Input
23968  * Bootstrap MonthField class
23969  * 
23970  * @cfg {String} language default en
23971  * 
23972  * @constructor
23973  * Create a new MonthField
23974  * @param {Object} config The config object
23975  */
23976
23977 Roo.bootstrap.MonthField = function(config){
23978     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23979     
23980     this.addEvents({
23981         /**
23982          * @event show
23983          * Fires when this field show.
23984          * @param {Roo.bootstrap.MonthField} this
23985          * @param {Mixed} date The date value
23986          */
23987         show : true,
23988         /**
23989          * @event show
23990          * Fires when this field hide.
23991          * @param {Roo.bootstrap.MonthField} this
23992          * @param {Mixed} date The date value
23993          */
23994         hide : true,
23995         /**
23996          * @event select
23997          * Fires when select a date.
23998          * @param {Roo.bootstrap.MonthField} this
23999          * @param {String} oldvalue The old value
24000          * @param {String} newvalue The new value
24001          */
24002         select : true
24003     });
24004 };
24005
24006 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24007     
24008     onRender: function(ct, position)
24009     {
24010         
24011         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24012         
24013         this.language = this.language || 'en';
24014         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24015         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24016         
24017         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24018         this.isInline = false;
24019         this.isInput = true;
24020         this.component = this.el.select('.add-on', true).first() || false;
24021         this.component = (this.component && this.component.length === 0) ? false : this.component;
24022         this.hasInput = this.component && this.inputEL().length;
24023         
24024         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24025         
24026         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24027         
24028         this.picker().on('mousedown', this.onMousedown, this);
24029         this.picker().on('click', this.onClick, this);
24030         
24031         this.picker().addClass('datepicker-dropdown');
24032         
24033         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24034             v.setStyle('width', '189px');
24035         });
24036         
24037         this.fillMonths();
24038         
24039         this.update();
24040         
24041         if(this.isInline) {
24042             this.show();
24043         }
24044         
24045     },
24046     
24047     setValue: function(v, suppressEvent)
24048     {   
24049         var o = this.getValue();
24050         
24051         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24052         
24053         this.update();
24054
24055         if(suppressEvent !== true){
24056             this.fireEvent('select', this, o, v);
24057         }
24058         
24059     },
24060     
24061     getValue: function()
24062     {
24063         return this.value;
24064     },
24065     
24066     onClick: function(e) 
24067     {
24068         e.stopPropagation();
24069         e.preventDefault();
24070         
24071         var target = e.getTarget();
24072         
24073         if(target.nodeName.toLowerCase() === 'i'){
24074             target = Roo.get(target).dom.parentNode;
24075         }
24076         
24077         var nodeName = target.nodeName;
24078         var className = target.className;
24079         var html = target.innerHTML;
24080         
24081         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24082             return;
24083         }
24084         
24085         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24086         
24087         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24088         
24089         this.hide();
24090                         
24091     },
24092     
24093     picker : function()
24094     {
24095         return this.pickerEl;
24096     },
24097     
24098     fillMonths: function()
24099     {    
24100         var i = 0;
24101         var months = this.picker().select('>.datepicker-months td', true).first();
24102         
24103         months.dom.innerHTML = '';
24104         
24105         while (i < 12) {
24106             var month = {
24107                 tag: 'span',
24108                 cls: 'month',
24109                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24110             };
24111             
24112             months.createChild(month);
24113         }
24114         
24115     },
24116     
24117     update: function()
24118     {
24119         var _this = this;
24120         
24121         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24122             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24123         }
24124         
24125         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24126             e.removeClass('active');
24127             
24128             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24129                 e.addClass('active');
24130             }
24131         })
24132     },
24133     
24134     place: function()
24135     {
24136         if(this.isInline) {
24137             return;
24138         }
24139         
24140         this.picker().removeClass(['bottom', 'top']);
24141         
24142         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24143             /*
24144              * place to the top of element!
24145              *
24146              */
24147             
24148             this.picker().addClass('top');
24149             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24150             
24151             return;
24152         }
24153         
24154         this.picker().addClass('bottom');
24155         
24156         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24157     },
24158     
24159     onFocus : function()
24160     {
24161         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24162         this.show();
24163     },
24164     
24165     onBlur : function()
24166     {
24167         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24168         
24169         var d = this.inputEl().getValue();
24170         
24171         this.setValue(d);
24172                 
24173         this.hide();
24174     },
24175     
24176     show : function()
24177     {
24178         this.picker().show();
24179         this.picker().select('>.datepicker-months', true).first().show();
24180         this.update();
24181         this.place();
24182         
24183         this.fireEvent('show', this, this.date);
24184     },
24185     
24186     hide : function()
24187     {
24188         if(this.isInline) {
24189             return;
24190         }
24191         this.picker().hide();
24192         this.fireEvent('hide', this, this.date);
24193         
24194     },
24195     
24196     onMousedown: function(e)
24197     {
24198         e.stopPropagation();
24199         e.preventDefault();
24200     },
24201     
24202     keyup: function(e)
24203     {
24204         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24205         this.update();
24206     },
24207
24208     fireKey: function(e)
24209     {
24210         if (!this.picker().isVisible()){
24211             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24212                 this.show();
24213             }
24214             return;
24215         }
24216         
24217         var dir;
24218         
24219         switch(e.keyCode){
24220             case 27: // escape
24221                 this.hide();
24222                 e.preventDefault();
24223                 break;
24224             case 37: // left
24225             case 39: // right
24226                 dir = e.keyCode == 37 ? -1 : 1;
24227                 
24228                 this.vIndex = this.vIndex + dir;
24229                 
24230                 if(this.vIndex < 0){
24231                     this.vIndex = 0;
24232                 }
24233                 
24234                 if(this.vIndex > 11){
24235                     this.vIndex = 11;
24236                 }
24237                 
24238                 if(isNaN(this.vIndex)){
24239                     this.vIndex = 0;
24240                 }
24241                 
24242                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24243                 
24244                 break;
24245             case 38: // up
24246             case 40: // down
24247                 
24248                 dir = e.keyCode == 38 ? -1 : 1;
24249                 
24250                 this.vIndex = this.vIndex + dir * 4;
24251                 
24252                 if(this.vIndex < 0){
24253                     this.vIndex = 0;
24254                 }
24255                 
24256                 if(this.vIndex > 11){
24257                     this.vIndex = 11;
24258                 }
24259                 
24260                 if(isNaN(this.vIndex)){
24261                     this.vIndex = 0;
24262                 }
24263                 
24264                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265                 break;
24266                 
24267             case 13: // enter
24268                 
24269                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24270                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24271                 }
24272                 
24273                 this.hide();
24274                 e.preventDefault();
24275                 break;
24276             case 9: // tab
24277                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24278                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24279                 }
24280                 this.hide();
24281                 break;
24282             case 16: // shift
24283             case 17: // ctrl
24284             case 18: // alt
24285                 break;
24286             default :
24287                 this.hide();
24288                 
24289         }
24290     },
24291     
24292     remove: function() 
24293     {
24294         this.picker().remove();
24295     }
24296    
24297 });
24298
24299 Roo.apply(Roo.bootstrap.MonthField,  {
24300     
24301     content : {
24302         tag: 'tbody',
24303         cn: [
24304         {
24305             tag: 'tr',
24306             cn: [
24307             {
24308                 tag: 'td',
24309                 colspan: '7'
24310             }
24311             ]
24312         }
24313         ]
24314     },
24315     
24316     dates:{
24317         en: {
24318             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24319             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24320         }
24321     }
24322 });
24323
24324 Roo.apply(Roo.bootstrap.MonthField,  {
24325   
24326     template : {
24327         tag: 'div',
24328         cls: 'datepicker dropdown-menu roo-dynamic',
24329         cn: [
24330             {
24331                 tag: 'div',
24332                 cls: 'datepicker-months',
24333                 cn: [
24334                 {
24335                     tag: 'table',
24336                     cls: 'table-condensed',
24337                     cn:[
24338                         Roo.bootstrap.DateField.content
24339                     ]
24340                 }
24341                 ]
24342             }
24343         ]
24344     }
24345 });
24346
24347  
24348
24349  
24350  /*
24351  * - LGPL
24352  *
24353  * CheckBox
24354  * 
24355  */
24356
24357 /**
24358  * @class Roo.bootstrap.CheckBox
24359  * @extends Roo.bootstrap.Input
24360  * Bootstrap CheckBox class
24361  * 
24362  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24363  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24364  * @cfg {String} boxLabel The text that appears beside the checkbox
24365  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24366  * @cfg {Boolean} checked initnal the element
24367  * @cfg {Boolean} inline inline the element (default false)
24368  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24369  * @cfg {String} tooltip label tooltip
24370  * 
24371  * @constructor
24372  * Create a new CheckBox
24373  * @param {Object} config The config object
24374  */
24375
24376 Roo.bootstrap.CheckBox = function(config){
24377     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24378    
24379     this.addEvents({
24380         /**
24381         * @event check
24382         * Fires when the element is checked or unchecked.
24383         * @param {Roo.bootstrap.CheckBox} this This input
24384         * @param {Boolean} checked The new checked value
24385         */
24386        check : true,
24387        /**
24388         * @event click
24389         * Fires when the element is click.
24390         * @param {Roo.bootstrap.CheckBox} this This input
24391         */
24392        click : true
24393     });
24394     
24395 };
24396
24397 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24398   
24399     inputType: 'checkbox',
24400     inputValue: 1,
24401     valueOff: 0,
24402     boxLabel: false,
24403     checked: false,
24404     weight : false,
24405     inline: false,
24406     tooltip : '',
24407     
24408     // checkbox success does not make any sense really.. 
24409     invalidClass : "",
24410     validClass : "",
24411     
24412     
24413     getAutoCreate : function()
24414     {
24415         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24416         
24417         var id = Roo.id();
24418         
24419         var cfg = {};
24420         
24421         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24422         
24423         if(this.inline){
24424             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24425         }
24426         
24427         var input =  {
24428             tag: 'input',
24429             id : id,
24430             type : this.inputType,
24431             value : this.inputValue,
24432             cls : 'roo-' + this.inputType, //'form-box',
24433             placeholder : this.placeholder || ''
24434             
24435         };
24436         
24437         if(this.inputType != 'radio'){
24438             var hidden =  {
24439                 tag: 'input',
24440                 type : 'hidden',
24441                 cls : 'roo-hidden-value',
24442                 value : this.checked ? this.inputValue : this.valueOff
24443             };
24444         }
24445         
24446             
24447         if (this.weight) { // Validity check?
24448             cfg.cls += " " + this.inputType + "-" + this.weight;
24449         }
24450         
24451         if (this.disabled) {
24452             input.disabled=true;
24453         }
24454         
24455         if(this.checked){
24456             input.checked = this.checked;
24457         }
24458         
24459         if (this.name) {
24460             
24461             input.name = this.name;
24462             
24463             if(this.inputType != 'radio'){
24464                 hidden.name = this.name;
24465                 input.name = '_hidden_' + this.name;
24466             }
24467         }
24468         
24469         if (this.size) {
24470             input.cls += ' input-' + this.size;
24471         }
24472         
24473         var settings=this;
24474         
24475         ['xs','sm','md','lg'].map(function(size){
24476             if (settings[size]) {
24477                 cfg.cls += ' col-' + size + '-' + settings[size];
24478             }
24479         });
24480         
24481         var inputblock = input;
24482          
24483         if (this.before || this.after) {
24484             
24485             inputblock = {
24486                 cls : 'input-group',
24487                 cn :  [] 
24488             };
24489             
24490             if (this.before) {
24491                 inputblock.cn.push({
24492                     tag :'span',
24493                     cls : 'input-group-addon',
24494                     html : this.before
24495                 });
24496             }
24497             
24498             inputblock.cn.push(input);
24499             
24500             if(this.inputType != 'radio'){
24501                 inputblock.cn.push(hidden);
24502             }
24503             
24504             if (this.after) {
24505                 inputblock.cn.push({
24506                     tag :'span',
24507                     cls : 'input-group-addon',
24508                     html : this.after
24509                 });
24510             }
24511             
24512         }
24513         var boxLabelCfg = false;
24514         
24515         if(this.boxLabel){
24516            
24517             boxLabelCfg = {
24518                 tag: 'label',
24519                 //'for': id, // box label is handled by onclick - so no for...
24520                 cls: 'box-label',
24521                 html: this.boxLabel
24522             };
24523             if(this.tooltip){
24524                 boxLabelCfg.tooltip = this.tooltip;
24525             }
24526              
24527         }
24528         
24529         
24530         if (align ==='left' && this.fieldLabel.length) {
24531 //                Roo.log("left and has label");
24532             cfg.cn = [
24533                 {
24534                     tag: 'label',
24535                     'for' :  id,
24536                     cls : 'control-label',
24537                     html : this.fieldLabel
24538                 },
24539                 {
24540                     cls : "", 
24541                     cn: [
24542                         inputblock
24543                     ]
24544                 }
24545             ];
24546             
24547             if (boxLabelCfg) {
24548                 cfg.cn[1].cn.push(boxLabelCfg);
24549             }
24550             
24551             if(this.labelWidth > 12){
24552                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24553             }
24554             
24555             if(this.labelWidth < 13 && this.labelmd == 0){
24556                 this.labelmd = this.labelWidth;
24557             }
24558             
24559             if(this.labellg > 0){
24560                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24561                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24562             }
24563             
24564             if(this.labelmd > 0){
24565                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24566                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24567             }
24568             
24569             if(this.labelsm > 0){
24570                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24571                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24572             }
24573             
24574             if(this.labelxs > 0){
24575                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24576                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24577             }
24578             
24579         } else if ( this.fieldLabel.length) {
24580 //                Roo.log(" label");
24581                 cfg.cn = [
24582                    
24583                     {
24584                         tag: this.boxLabel ? 'span' : 'label',
24585                         'for': id,
24586                         cls: 'control-label box-input-label',
24587                         //cls : 'input-group-addon',
24588                         html : this.fieldLabel
24589                     },
24590                     
24591                     inputblock
24592                     
24593                 ];
24594                 if (boxLabelCfg) {
24595                     cfg.cn.push(boxLabelCfg);
24596                 }
24597
24598         } else {
24599             
24600 //                Roo.log(" no label && no align");
24601                 cfg.cn = [  inputblock ] ;
24602                 if (boxLabelCfg) {
24603                     cfg.cn.push(boxLabelCfg);
24604                 }
24605
24606                 
24607         }
24608         
24609        
24610         
24611         if(this.inputType != 'radio'){
24612             cfg.cn.push(hidden);
24613         }
24614         
24615         return cfg;
24616         
24617     },
24618     
24619     /**
24620      * return the real input element.
24621      */
24622     inputEl: function ()
24623     {
24624         return this.el.select('input.roo-' + this.inputType,true).first();
24625     },
24626     hiddenEl: function ()
24627     {
24628         return this.el.select('input.roo-hidden-value',true).first();
24629     },
24630     
24631     labelEl: function()
24632     {
24633         return this.el.select('label.control-label',true).first();
24634     },
24635     /* depricated... */
24636     
24637     label: function()
24638     {
24639         return this.labelEl();
24640     },
24641     
24642     boxLabelEl: function()
24643     {
24644         return this.el.select('label.box-label',true).first();
24645     },
24646     
24647     initEvents : function()
24648     {
24649 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24650         
24651         this.inputEl().on('click', this.onClick,  this);
24652         
24653         if (this.boxLabel) { 
24654             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24655         }
24656         
24657         this.startValue = this.getValue();
24658         
24659         if(this.groupId){
24660             Roo.bootstrap.CheckBox.register(this);
24661         }
24662     },
24663     
24664     onClick : function(e)
24665     {   
24666         if(this.fireEvent('click', this, e) !== false){
24667             this.setChecked(!this.checked);
24668         }
24669         
24670     },
24671     
24672     setChecked : function(state,suppressEvent)
24673     {
24674         this.startValue = this.getValue();
24675
24676         if(this.inputType == 'radio'){
24677             
24678             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24679                 e.dom.checked = false;
24680             });
24681             
24682             this.inputEl().dom.checked = true;
24683             
24684             this.inputEl().dom.value = this.inputValue;
24685             
24686             if(suppressEvent !== true){
24687                 this.fireEvent('check', this, true);
24688             }
24689             
24690             this.validate();
24691             
24692             return;
24693         }
24694         
24695         this.checked = state;
24696         
24697         this.inputEl().dom.checked = state;
24698         
24699         
24700         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24701         
24702         if(suppressEvent !== true){
24703             this.fireEvent('check', this, state);
24704         }
24705         
24706         this.validate();
24707     },
24708     
24709     getValue : function()
24710     {
24711         if(this.inputType == 'radio'){
24712             return this.getGroupValue();
24713         }
24714         
24715         return this.hiddenEl().dom.value;
24716         
24717     },
24718     
24719     getGroupValue : function()
24720     {
24721         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24722             return '';
24723         }
24724         
24725         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24726     },
24727     
24728     setValue : function(v,suppressEvent)
24729     {
24730         if(this.inputType == 'radio'){
24731             this.setGroupValue(v, suppressEvent);
24732             return;
24733         }
24734         
24735         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24736         
24737         this.validate();
24738     },
24739     
24740     setGroupValue : function(v, suppressEvent)
24741     {
24742         this.startValue = this.getValue();
24743         
24744         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24745             e.dom.checked = false;
24746             
24747             if(e.dom.value == v){
24748                 e.dom.checked = true;
24749             }
24750         });
24751         
24752         if(suppressEvent !== true){
24753             this.fireEvent('check', this, true);
24754         }
24755
24756         this.validate();
24757         
24758         return;
24759     },
24760     
24761     validate : function()
24762     {
24763         if(this.getVisibilityEl().hasClass('hidden')){
24764             return true;
24765         }
24766         
24767         if(
24768                 this.disabled || 
24769                 (this.inputType == 'radio' && this.validateRadio()) ||
24770                 (this.inputType == 'checkbox' && this.validateCheckbox())
24771         ){
24772             this.markValid();
24773             return true;
24774         }
24775         
24776         this.markInvalid();
24777         return false;
24778     },
24779     
24780     validateRadio : function()
24781     {
24782         if(this.getVisibilityEl().hasClass('hidden')){
24783             return true;
24784         }
24785         
24786         if(this.allowBlank){
24787             return true;
24788         }
24789         
24790         var valid = false;
24791         
24792         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24793             if(!e.dom.checked){
24794                 return;
24795             }
24796             
24797             valid = true;
24798             
24799             return false;
24800         });
24801         
24802         return valid;
24803     },
24804     
24805     validateCheckbox : function()
24806     {
24807         if(!this.groupId){
24808             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24809             //return (this.getValue() == this.inputValue) ? true : false;
24810         }
24811         
24812         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24813         
24814         if(!group){
24815             return false;
24816         }
24817         
24818         var r = false;
24819         
24820         for(var i in group){
24821             if(group[i].el.isVisible(true)){
24822                 r = false;
24823                 break;
24824             }
24825             
24826             r = true;
24827         }
24828         
24829         for(var i in group){
24830             if(r){
24831                 break;
24832             }
24833             
24834             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24835         }
24836         
24837         return r;
24838     },
24839     
24840     /**
24841      * Mark this field as valid
24842      */
24843     markValid : function()
24844     {
24845         var _this = this;
24846         
24847         this.fireEvent('valid', this);
24848         
24849         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24850         
24851         if(this.groupId){
24852             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24853         }
24854         
24855         if(label){
24856             label.markValid();
24857         }
24858
24859         if(this.inputType == 'radio'){
24860             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24861                 var fg = e.findParent('.form-group', false, true);
24862                 if (Roo.bootstrap.version == 3) {
24863                     fg.removeClass([_this.invalidClass, _this.validClass]);
24864                     fg.addClass(_this.validClass);
24865                 } else {
24866                     fg.removeClass(['is-valid', 'is-invalid']);
24867                     fg.addClass('is-valid');
24868                 }
24869             });
24870             
24871             return;
24872         }
24873
24874         if(!this.groupId){
24875             var fg = this.el.findParent('.form-group', false, true);
24876             if (Roo.bootstrap.version == 3) {
24877                 fg.removeClass([this.invalidClass, this.validClass]);
24878                 fg.addClass(this.validClass);
24879             } else {
24880                 fg.removeClass(['is-valid', 'is-invalid']);
24881                 fg.addClass('is-valid');
24882             }
24883             return;
24884         }
24885         
24886         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24887         
24888         if(!group){
24889             return;
24890         }
24891         
24892         for(var i in group){
24893             var fg = group[i].el.findParent('.form-group', false, true);
24894             if (Roo.bootstrap.version == 3) {
24895                 fg.removeClass([this.invalidClass, this.validClass]);
24896                 fg.addClass(this.validClass);
24897             } else {
24898                 fg.removeClass(['is-valid', 'is-invalid']);
24899                 fg.addClass('is-valid');
24900             }
24901         }
24902     },
24903     
24904      /**
24905      * Mark this field as invalid
24906      * @param {String} msg The validation message
24907      */
24908     markInvalid : function(msg)
24909     {
24910         if(this.allowBlank){
24911             return;
24912         }
24913         
24914         var _this = this;
24915         
24916         this.fireEvent('invalid', this, msg);
24917         
24918         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24919         
24920         if(this.groupId){
24921             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24922         }
24923         
24924         if(label){
24925             label.markInvalid();
24926         }
24927             
24928         if(this.inputType == 'radio'){
24929             
24930             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24931                 var fg = e.findParent('.form-group', false, true);
24932                 if (Roo.bootstrap.version == 3) {
24933                     fg.removeClass([_this.invalidClass, _this.validClass]);
24934                     fg.addClass(_this.invalidClass);
24935                 } else {
24936                     fg.removeClass(['is-invalid', 'is-valid']);
24937                     fg.addClass('is-invalid');
24938                 }
24939             });
24940             
24941             return;
24942         }
24943         
24944         if(!this.groupId){
24945             var fg = this.el.findParent('.form-group', false, true);
24946             if (Roo.bootstrap.version == 3) {
24947                 fg.removeClass([_this.invalidClass, _this.validClass]);
24948                 fg.addClass(_this.invalidClass);
24949             } else {
24950                 fg.removeClass(['is-invalid', 'is-valid']);
24951                 fg.addClass('is-invalid');
24952             }
24953             return;
24954         }
24955         
24956         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24957         
24958         if(!group){
24959             return;
24960         }
24961         
24962         for(var i in group){
24963             var fg = group[i].el.findParent('.form-group', false, true);
24964             if (Roo.bootstrap.version == 3) {
24965                 fg.removeClass([_this.invalidClass, _this.validClass]);
24966                 fg.addClass(_this.invalidClass);
24967             } else {
24968                 fg.removeClass(['is-invalid', 'is-valid']);
24969                 fg.addClass('is-invalid');
24970             }
24971         }
24972         
24973     },
24974     
24975     clearInvalid : function()
24976     {
24977         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24978         
24979         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24980         
24981         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24982         
24983         if (label && label.iconEl) {
24984             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24985             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24986         }
24987     },
24988     
24989     disable : function()
24990     {
24991         if(this.inputType != 'radio'){
24992             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24993             return;
24994         }
24995         
24996         var _this = this;
24997         
24998         if(this.rendered){
24999             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25000                 _this.getActionEl().addClass(this.disabledClass);
25001                 e.dom.disabled = true;
25002             });
25003         }
25004         
25005         this.disabled = true;
25006         this.fireEvent("disable", this);
25007         return this;
25008     },
25009
25010     enable : function()
25011     {
25012         if(this.inputType != 'radio'){
25013             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25014             return;
25015         }
25016         
25017         var _this = this;
25018         
25019         if(this.rendered){
25020             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25021                 _this.getActionEl().removeClass(this.disabledClass);
25022                 e.dom.disabled = false;
25023             });
25024         }
25025         
25026         this.disabled = false;
25027         this.fireEvent("enable", this);
25028         return this;
25029     },
25030     
25031     setBoxLabel : function(v)
25032     {
25033         this.boxLabel = v;
25034         
25035         if(this.rendered){
25036             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25037         }
25038     }
25039
25040 });
25041
25042 Roo.apply(Roo.bootstrap.CheckBox, {
25043     
25044     groups: {},
25045     
25046      /**
25047     * register a CheckBox Group
25048     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25049     */
25050     register : function(checkbox)
25051     {
25052         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25053             this.groups[checkbox.groupId] = {};
25054         }
25055         
25056         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25057             return;
25058         }
25059         
25060         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25061         
25062     },
25063     /**
25064     * fetch a CheckBox Group based on the group ID
25065     * @param {string} the group ID
25066     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25067     */
25068     get: function(groupId) {
25069         if (typeof(this.groups[groupId]) == 'undefined') {
25070             return false;
25071         }
25072         
25073         return this.groups[groupId] ;
25074     }
25075     
25076     
25077 });
25078 /*
25079  * - LGPL
25080  *
25081  * RadioItem
25082  * 
25083  */
25084
25085 /**
25086  * @class Roo.bootstrap.Radio
25087  * @extends Roo.bootstrap.Component
25088  * Bootstrap Radio class
25089  * @cfg {String} boxLabel - the label associated
25090  * @cfg {String} value - the value of radio
25091  * 
25092  * @constructor
25093  * Create a new Radio
25094  * @param {Object} config The config object
25095  */
25096 Roo.bootstrap.Radio = function(config){
25097     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25098     
25099 };
25100
25101 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25102     
25103     boxLabel : '',
25104     
25105     value : '',
25106     
25107     getAutoCreate : function()
25108     {
25109         var cfg = {
25110             tag : 'div',
25111             cls : 'form-group radio',
25112             cn : [
25113                 {
25114                     tag : 'label',
25115                     cls : 'box-label',
25116                     html : this.boxLabel
25117                 }
25118             ]
25119         };
25120         
25121         return cfg;
25122     },
25123     
25124     initEvents : function() 
25125     {
25126         this.parent().register(this);
25127         
25128         this.el.on('click', this.onClick, this);
25129         
25130     },
25131     
25132     onClick : function(e)
25133     {
25134         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25135             this.setChecked(true);
25136         }
25137     },
25138     
25139     setChecked : function(state, suppressEvent)
25140     {
25141         this.parent().setValue(this.value, suppressEvent);
25142         
25143     },
25144     
25145     setBoxLabel : function(v)
25146     {
25147         this.boxLabel = v;
25148         
25149         if(this.rendered){
25150             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25151         }
25152     }
25153     
25154 });
25155  
25156
25157  /*
25158  * - LGPL
25159  *
25160  * Input
25161  * 
25162  */
25163
25164 /**
25165  * @class Roo.bootstrap.SecurePass
25166  * @extends Roo.bootstrap.Input
25167  * Bootstrap SecurePass class
25168  *
25169  * 
25170  * @constructor
25171  * Create a new SecurePass
25172  * @param {Object} config The config object
25173  */
25174  
25175 Roo.bootstrap.SecurePass = function (config) {
25176     // these go here, so the translation tool can replace them..
25177     this.errors = {
25178         PwdEmpty: "Please type a password, and then retype it to confirm.",
25179         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25180         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25181         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25182         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25183         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25184         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25185         TooWeak: "Your password is Too Weak."
25186     },
25187     this.meterLabel = "Password strength:";
25188     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25189     this.meterClass = [
25190         "roo-password-meter-tooweak", 
25191         "roo-password-meter-weak", 
25192         "roo-password-meter-medium", 
25193         "roo-password-meter-strong", 
25194         "roo-password-meter-grey"
25195     ];
25196     
25197     this.errors = {};
25198     
25199     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25200 }
25201
25202 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25203     /**
25204      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25205      * {
25206      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25207      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25208      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25209      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25210      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25211      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25212      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25213      * })
25214      */
25215     // private
25216     
25217     meterWidth: 300,
25218     errorMsg :'',    
25219     errors: false,
25220     imageRoot: '/',
25221     /**
25222      * @cfg {String/Object} Label for the strength meter (defaults to
25223      * 'Password strength:')
25224      */
25225     // private
25226     meterLabel: '',
25227     /**
25228      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25229      * ['Weak', 'Medium', 'Strong'])
25230      */
25231     // private    
25232     pwdStrengths: false,    
25233     // private
25234     strength: 0,
25235     // private
25236     _lastPwd: null,
25237     // private
25238     kCapitalLetter: 0,
25239     kSmallLetter: 1,
25240     kDigit: 2,
25241     kPunctuation: 3,
25242     
25243     insecure: false,
25244     // private
25245     initEvents: function ()
25246     {
25247         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25248
25249         if (this.el.is('input[type=password]') && Roo.isSafari) {
25250             this.el.on('keydown', this.SafariOnKeyDown, this);
25251         }
25252
25253         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25254     },
25255     // private
25256     onRender: function (ct, position)
25257     {
25258         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25259         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25260         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25261
25262         this.trigger.createChild({
25263                    cn: [
25264                     {
25265                     //id: 'PwdMeter',
25266                     tag: 'div',
25267                     cls: 'roo-password-meter-grey col-xs-12',
25268                     style: {
25269                         //width: 0,
25270                         //width: this.meterWidth + 'px'                                                
25271                         }
25272                     },
25273                     {                            
25274                          cls: 'roo-password-meter-text'                          
25275                     }
25276                 ]            
25277         });
25278
25279          
25280         if (this.hideTrigger) {
25281             this.trigger.setDisplayed(false);
25282         }
25283         this.setSize(this.width || '', this.height || '');
25284     },
25285     // private
25286     onDestroy: function ()
25287     {
25288         if (this.trigger) {
25289             this.trigger.removeAllListeners();
25290             this.trigger.remove();
25291         }
25292         if (this.wrap) {
25293             this.wrap.remove();
25294         }
25295         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25296     },
25297     // private
25298     checkStrength: function ()
25299     {
25300         var pwd = this.inputEl().getValue();
25301         if (pwd == this._lastPwd) {
25302             return;
25303         }
25304
25305         var strength;
25306         if (this.ClientSideStrongPassword(pwd)) {
25307             strength = 3;
25308         } else if (this.ClientSideMediumPassword(pwd)) {
25309             strength = 2;
25310         } else if (this.ClientSideWeakPassword(pwd)) {
25311             strength = 1;
25312         } else {
25313             strength = 0;
25314         }
25315         
25316         Roo.log('strength1: ' + strength);
25317         
25318         //var pm = this.trigger.child('div/div/div').dom;
25319         var pm = this.trigger.child('div/div');
25320         pm.removeClass(this.meterClass);
25321         pm.addClass(this.meterClass[strength]);
25322                 
25323         
25324         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25325                 
25326         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25327         
25328         this._lastPwd = pwd;
25329     },
25330     reset: function ()
25331     {
25332         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25333         
25334         this._lastPwd = '';
25335         
25336         var pm = this.trigger.child('div/div');
25337         pm.removeClass(this.meterClass);
25338         pm.addClass('roo-password-meter-grey');        
25339         
25340         
25341         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25342         
25343         pt.innerHTML = '';
25344         this.inputEl().dom.type='password';
25345     },
25346     // private
25347     validateValue: function (value)
25348     {
25349         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25350             return false;
25351         }
25352         if (value.length == 0) {
25353             if (this.allowBlank) {
25354                 this.clearInvalid();
25355                 return true;
25356             }
25357
25358             this.markInvalid(this.errors.PwdEmpty);
25359             this.errorMsg = this.errors.PwdEmpty;
25360             return false;
25361         }
25362         
25363         if(this.insecure){
25364             return true;
25365         }
25366         
25367         if (!value.match(/[\x21-\x7e]+/)) {
25368             this.markInvalid(this.errors.PwdBadChar);
25369             this.errorMsg = this.errors.PwdBadChar;
25370             return false;
25371         }
25372         if (value.length < 6) {
25373             this.markInvalid(this.errors.PwdShort);
25374             this.errorMsg = this.errors.PwdShort;
25375             return false;
25376         }
25377         if (value.length > 16) {
25378             this.markInvalid(this.errors.PwdLong);
25379             this.errorMsg = this.errors.PwdLong;
25380             return false;
25381         }
25382         var strength;
25383         if (this.ClientSideStrongPassword(value)) {
25384             strength = 3;
25385         } else if (this.ClientSideMediumPassword(value)) {
25386             strength = 2;
25387         } else if (this.ClientSideWeakPassword(value)) {
25388             strength = 1;
25389         } else {
25390             strength = 0;
25391         }
25392
25393         
25394         if (strength < 2) {
25395             //this.markInvalid(this.errors.TooWeak);
25396             this.errorMsg = this.errors.TooWeak;
25397             //return false;
25398         }
25399         
25400         
25401         console.log('strength2: ' + strength);
25402         
25403         //var pm = this.trigger.child('div/div/div').dom;
25404         
25405         var pm = this.trigger.child('div/div');
25406         pm.removeClass(this.meterClass);
25407         pm.addClass(this.meterClass[strength]);
25408                 
25409         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25410                 
25411         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25412         
25413         this.errorMsg = ''; 
25414         return true;
25415     },
25416     // private
25417     CharacterSetChecks: function (type)
25418     {
25419         this.type = type;
25420         this.fResult = false;
25421     },
25422     // private
25423     isctype: function (character, type)
25424     {
25425         switch (type) {  
25426             case this.kCapitalLetter:
25427                 if (character >= 'A' && character <= 'Z') {
25428                     return true;
25429                 }
25430                 break;
25431             
25432             case this.kSmallLetter:
25433                 if (character >= 'a' && character <= 'z') {
25434                     return true;
25435                 }
25436                 break;
25437             
25438             case this.kDigit:
25439                 if (character >= '0' && character <= '9') {
25440                     return true;
25441                 }
25442                 break;
25443             
25444             case this.kPunctuation:
25445                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25446                     return true;
25447                 }
25448                 break;
25449             
25450             default:
25451                 return false;
25452         }
25453
25454     },
25455     // private
25456     IsLongEnough: function (pwd, size)
25457     {
25458         return !(pwd == null || isNaN(size) || pwd.length < size);
25459     },
25460     // private
25461     SpansEnoughCharacterSets: function (word, nb)
25462     {
25463         if (!this.IsLongEnough(word, nb))
25464         {
25465             return false;
25466         }
25467
25468         var characterSetChecks = new Array(
25469             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25470             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25471         );
25472         
25473         for (var index = 0; index < word.length; ++index) {
25474             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25475                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25476                     characterSetChecks[nCharSet].fResult = true;
25477                     break;
25478                 }
25479             }
25480         }
25481
25482         var nCharSets = 0;
25483         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25484             if (characterSetChecks[nCharSet].fResult) {
25485                 ++nCharSets;
25486             }
25487         }
25488
25489         if (nCharSets < nb) {
25490             return false;
25491         }
25492         return true;
25493     },
25494     // private
25495     ClientSideStrongPassword: function (pwd)
25496     {
25497         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25498     },
25499     // private
25500     ClientSideMediumPassword: function (pwd)
25501     {
25502         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25503     },
25504     // private
25505     ClientSideWeakPassword: function (pwd)
25506     {
25507         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25508     }
25509           
25510 })//<script type="text/javascript">
25511
25512 /*
25513  * Based  Ext JS Library 1.1.1
25514  * Copyright(c) 2006-2007, Ext JS, LLC.
25515  * LGPL
25516  *
25517  */
25518  
25519 /**
25520  * @class Roo.HtmlEditorCore
25521  * @extends Roo.Component
25522  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25523  *
25524  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25525  */
25526
25527 Roo.HtmlEditorCore = function(config){
25528     
25529     
25530     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25531     
25532     
25533     this.addEvents({
25534         /**
25535          * @event initialize
25536          * Fires when the editor is fully initialized (including the iframe)
25537          * @param {Roo.HtmlEditorCore} this
25538          */
25539         initialize: true,
25540         /**
25541          * @event activate
25542          * Fires when the editor is first receives the focus. Any insertion must wait
25543          * until after this event.
25544          * @param {Roo.HtmlEditorCore} this
25545          */
25546         activate: true,
25547          /**
25548          * @event beforesync
25549          * Fires before the textarea is updated with content from the editor iframe. Return false
25550          * to cancel the sync.
25551          * @param {Roo.HtmlEditorCore} this
25552          * @param {String} html
25553          */
25554         beforesync: true,
25555          /**
25556          * @event beforepush
25557          * Fires before the iframe editor is updated with content from the textarea. Return false
25558          * to cancel the push.
25559          * @param {Roo.HtmlEditorCore} this
25560          * @param {String} html
25561          */
25562         beforepush: true,
25563          /**
25564          * @event sync
25565          * Fires when the textarea is updated with content from the editor iframe.
25566          * @param {Roo.HtmlEditorCore} this
25567          * @param {String} html
25568          */
25569         sync: true,
25570          /**
25571          * @event push
25572          * Fires when the iframe editor is updated with content from the textarea.
25573          * @param {Roo.HtmlEditorCore} this
25574          * @param {String} html
25575          */
25576         push: true,
25577         
25578         /**
25579          * @event editorevent
25580          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25581          * @param {Roo.HtmlEditorCore} this
25582          */
25583         editorevent: true
25584         
25585     });
25586     
25587     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25588     
25589     // defaults : white / black...
25590     this.applyBlacklists();
25591     
25592     
25593     
25594 };
25595
25596
25597 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25598
25599
25600      /**
25601      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25602      */
25603     
25604     owner : false,
25605     
25606      /**
25607      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25608      *                        Roo.resizable.
25609      */
25610     resizable : false,
25611      /**
25612      * @cfg {Number} height (in pixels)
25613      */   
25614     height: 300,
25615    /**
25616      * @cfg {Number} width (in pixels)
25617      */   
25618     width: 500,
25619     
25620     /**
25621      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25622      * 
25623      */
25624     stylesheets: false,
25625     
25626     /**
25627      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25628      */
25629     allowComments: false,
25630     // id of frame..
25631     frameId: false,
25632     
25633     // private properties
25634     validationEvent : false,
25635     deferHeight: true,
25636     initialized : false,
25637     activated : false,
25638     sourceEditMode : false,
25639     onFocus : Roo.emptyFn,
25640     iframePad:3,
25641     hideMode:'offsets',
25642     
25643     clearUp: true,
25644     
25645     // blacklist + whitelisted elements..
25646     black: false,
25647     white: false,
25648      
25649     bodyCls : '',
25650
25651     /**
25652      * Protected method that will not generally be called directly. It
25653      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25654      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25655      */
25656     getDocMarkup : function(){
25657         // body styles..
25658         var st = '';
25659         
25660         // inherit styels from page...?? 
25661         if (this.stylesheets === false) {
25662             
25663             Roo.get(document.head).select('style').each(function(node) {
25664                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25665             });
25666             
25667             Roo.get(document.head).select('link').each(function(node) { 
25668                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25669             });
25670             
25671         } else if (!this.stylesheets.length) {
25672                 // simple..
25673                 st = '<style type="text/css">' +
25674                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25675                    '</style>';
25676         } else {
25677             for (var i in this.stylesheets) { 
25678                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25679             }
25680             
25681         }
25682         
25683         st +=  '<style type="text/css">' +
25684             'IMG { cursor: pointer } ' +
25685         '</style>';
25686
25687         var cls = 'roo-htmleditor-body';
25688         
25689         if(this.bodyCls.length){
25690             cls += ' ' + this.bodyCls;
25691         }
25692         
25693         return '<html><head>' + st  +
25694             //<style type="text/css">' +
25695             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25696             //'</style>' +
25697             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25698     },
25699
25700     // private
25701     onRender : function(ct, position)
25702     {
25703         var _t = this;
25704         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25705         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25706         
25707         
25708         this.el.dom.style.border = '0 none';
25709         this.el.dom.setAttribute('tabIndex', -1);
25710         this.el.addClass('x-hidden hide');
25711         
25712         
25713         
25714         if(Roo.isIE){ // fix IE 1px bogus margin
25715             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25716         }
25717        
25718         
25719         this.frameId = Roo.id();
25720         
25721          
25722         
25723         var iframe = this.owner.wrap.createChild({
25724             tag: 'iframe',
25725             cls: 'form-control', // bootstrap..
25726             id: this.frameId,
25727             name: this.frameId,
25728             frameBorder : 'no',
25729             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25730         }, this.el
25731         );
25732         
25733         
25734         this.iframe = iframe.dom;
25735
25736          this.assignDocWin();
25737         
25738         this.doc.designMode = 'on';
25739        
25740         this.doc.open();
25741         this.doc.write(this.getDocMarkup());
25742         this.doc.close();
25743
25744         
25745         var task = { // must defer to wait for browser to be ready
25746             run : function(){
25747                 //console.log("run task?" + this.doc.readyState);
25748                 this.assignDocWin();
25749                 if(this.doc.body || this.doc.readyState == 'complete'){
25750                     try {
25751                         this.doc.designMode="on";
25752                     } catch (e) {
25753                         return;
25754                     }
25755                     Roo.TaskMgr.stop(task);
25756                     this.initEditor.defer(10, this);
25757                 }
25758             },
25759             interval : 10,
25760             duration: 10000,
25761             scope: this
25762         };
25763         Roo.TaskMgr.start(task);
25764
25765     },
25766
25767     // private
25768     onResize : function(w, h)
25769     {
25770          Roo.log('resize: ' +w + ',' + h );
25771         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25772         if(!this.iframe){
25773             return;
25774         }
25775         if(typeof w == 'number'){
25776             
25777             this.iframe.style.width = w + 'px';
25778         }
25779         if(typeof h == 'number'){
25780             
25781             this.iframe.style.height = h + 'px';
25782             if(this.doc){
25783                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25784             }
25785         }
25786         
25787     },
25788
25789     /**
25790      * Toggles the editor between standard and source edit mode.
25791      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25792      */
25793     toggleSourceEdit : function(sourceEditMode){
25794         
25795         this.sourceEditMode = sourceEditMode === true;
25796         
25797         if(this.sourceEditMode){
25798  
25799             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25800             
25801         }else{
25802             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25803             //this.iframe.className = '';
25804             this.deferFocus();
25805         }
25806         //this.setSize(this.owner.wrap.getSize());
25807         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25808     },
25809
25810     
25811   
25812
25813     /**
25814      * Protected method that will not generally be called directly. If you need/want
25815      * custom HTML cleanup, this is the method you should override.
25816      * @param {String} html The HTML to be cleaned
25817      * return {String} The cleaned HTML
25818      */
25819     cleanHtml : function(html){
25820         html = String(html);
25821         if(html.length > 5){
25822             if(Roo.isSafari){ // strip safari nonsense
25823                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25824             }
25825         }
25826         if(html == '&nbsp;'){
25827             html = '';
25828         }
25829         return html;
25830     },
25831
25832     /**
25833      * HTML Editor -> Textarea
25834      * Protected method that will not generally be called directly. Syncs the contents
25835      * of the editor iframe with the textarea.
25836      */
25837     syncValue : function(){
25838         if(this.initialized){
25839             var bd = (this.doc.body || this.doc.documentElement);
25840             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25841             var html = bd.innerHTML;
25842             if(Roo.isSafari){
25843                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25844                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25845                 if(m && m[1]){
25846                     html = '<div style="'+m[0]+'">' + html + '</div>';
25847                 }
25848             }
25849             html = this.cleanHtml(html);
25850             // fix up the special chars.. normaly like back quotes in word...
25851             // however we do not want to do this with chinese..
25852             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25853                 
25854                 var cc = match.charCodeAt();
25855
25856                 // Get the character value, handling surrogate pairs
25857                 if (match.length == 2) {
25858                     // It's a surrogate pair, calculate the Unicode code point
25859                     var high = match.charCodeAt(0) - 0xD800;
25860                     var low  = match.charCodeAt(1) - 0xDC00;
25861                     cc = (high * 0x400) + low + 0x10000;
25862                 }  else if (
25863                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25864                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25865                     (cc >= 0xf900 && cc < 0xfb00 )
25866                 ) {
25867                         return match;
25868                 }  
25869          
25870                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25871                 return "&#" + cc + ";";
25872                 
25873                 
25874             });
25875             
25876             
25877              
25878             if(this.owner.fireEvent('beforesync', this, html) !== false){
25879                 this.el.dom.value = html;
25880                 this.owner.fireEvent('sync', this, html);
25881             }
25882         }
25883     },
25884
25885     /**
25886      * Protected method that will not generally be called directly. Pushes the value of the textarea
25887      * into the iframe editor.
25888      */
25889     pushValue : function(){
25890         if(this.initialized){
25891             var v = this.el.dom.value.trim();
25892             
25893 //            if(v.length < 1){
25894 //                v = '&#160;';
25895 //            }
25896             
25897             if(this.owner.fireEvent('beforepush', this, v) !== false){
25898                 var d = (this.doc.body || this.doc.documentElement);
25899                 d.innerHTML = v;
25900                 this.cleanUpPaste();
25901                 this.el.dom.value = d.innerHTML;
25902                 this.owner.fireEvent('push', this, v);
25903             }
25904         }
25905     },
25906
25907     // private
25908     deferFocus : function(){
25909         this.focus.defer(10, this);
25910     },
25911
25912     // doc'ed in Field
25913     focus : function(){
25914         if(this.win && !this.sourceEditMode){
25915             this.win.focus();
25916         }else{
25917             this.el.focus();
25918         }
25919     },
25920     
25921     assignDocWin: function()
25922     {
25923         var iframe = this.iframe;
25924         
25925          if(Roo.isIE){
25926             this.doc = iframe.contentWindow.document;
25927             this.win = iframe.contentWindow;
25928         } else {
25929 //            if (!Roo.get(this.frameId)) {
25930 //                return;
25931 //            }
25932 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25933 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25934             
25935             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25936                 return;
25937             }
25938             
25939             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25940             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25941         }
25942     },
25943     
25944     // private
25945     initEditor : function(){
25946         //console.log("INIT EDITOR");
25947         this.assignDocWin();
25948         
25949         
25950         
25951         this.doc.designMode="on";
25952         this.doc.open();
25953         this.doc.write(this.getDocMarkup());
25954         this.doc.close();
25955         
25956         var dbody = (this.doc.body || this.doc.documentElement);
25957         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25958         // this copies styles from the containing element into thsi one..
25959         // not sure why we need all of this..
25960         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25961         
25962         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25963         //ss['background-attachment'] = 'fixed'; // w3c
25964         dbody.bgProperties = 'fixed'; // ie
25965         //Roo.DomHelper.applyStyles(dbody, ss);
25966         Roo.EventManager.on(this.doc, {
25967             //'mousedown': this.onEditorEvent,
25968             'mouseup': this.onEditorEvent,
25969             'dblclick': this.onEditorEvent,
25970             'click': this.onEditorEvent,
25971             'keyup': this.onEditorEvent,
25972             buffer:100,
25973             scope: this
25974         });
25975         if(Roo.isGecko){
25976             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25977         }
25978         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25979             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25980         }
25981         this.initialized = true;
25982
25983         this.owner.fireEvent('initialize', this);
25984         this.pushValue();
25985     },
25986
25987     // private
25988     onDestroy : function(){
25989         
25990         
25991         
25992         if(this.rendered){
25993             
25994             //for (var i =0; i < this.toolbars.length;i++) {
25995             //    // fixme - ask toolbars for heights?
25996             //    this.toolbars[i].onDestroy();
25997            // }
25998             
25999             //this.wrap.dom.innerHTML = '';
26000             //this.wrap.remove();
26001         }
26002     },
26003
26004     // private
26005     onFirstFocus : function(){
26006         
26007         this.assignDocWin();
26008         
26009         
26010         this.activated = true;
26011          
26012     
26013         if(Roo.isGecko){ // prevent silly gecko errors
26014             this.win.focus();
26015             var s = this.win.getSelection();
26016             if(!s.focusNode || s.focusNode.nodeType != 3){
26017                 var r = s.getRangeAt(0);
26018                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26019                 r.collapse(true);
26020                 this.deferFocus();
26021             }
26022             try{
26023                 this.execCmd('useCSS', true);
26024                 this.execCmd('styleWithCSS', false);
26025             }catch(e){}
26026         }
26027         this.owner.fireEvent('activate', this);
26028     },
26029
26030     // private
26031     adjustFont: function(btn){
26032         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26033         //if(Roo.isSafari){ // safari
26034         //    adjust *= 2;
26035        // }
26036         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26037         if(Roo.isSafari){ // safari
26038             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26039             v =  (v < 10) ? 10 : v;
26040             v =  (v > 48) ? 48 : v;
26041             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26042             
26043         }
26044         
26045         
26046         v = Math.max(1, v+adjust);
26047         
26048         this.execCmd('FontSize', v  );
26049     },
26050
26051     onEditorEvent : function(e)
26052     {
26053         this.owner.fireEvent('editorevent', this, e);
26054       //  this.updateToolbar();
26055         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26056     },
26057
26058     insertTag : function(tg)
26059     {
26060         // could be a bit smarter... -> wrap the current selected tRoo..
26061         if (tg.toLowerCase() == 'span' ||
26062             tg.toLowerCase() == 'code' ||
26063             tg.toLowerCase() == 'sup' ||
26064             tg.toLowerCase() == 'sub' 
26065             ) {
26066             
26067             range = this.createRange(this.getSelection());
26068             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26069             wrappingNode.appendChild(range.extractContents());
26070             range.insertNode(wrappingNode);
26071
26072             return;
26073             
26074             
26075             
26076         }
26077         this.execCmd("formatblock",   tg);
26078         
26079     },
26080     
26081     insertText : function(txt)
26082     {
26083         
26084         
26085         var range = this.createRange();
26086         range.deleteContents();
26087                //alert(Sender.getAttribute('label'));
26088                
26089         range.insertNode(this.doc.createTextNode(txt));
26090     } ,
26091     
26092      
26093
26094     /**
26095      * Executes a Midas editor command on the editor document and performs necessary focus and
26096      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26097      * @param {String} cmd The Midas command
26098      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26099      */
26100     relayCmd : function(cmd, value){
26101         this.win.focus();
26102         this.execCmd(cmd, value);
26103         this.owner.fireEvent('editorevent', this);
26104         //this.updateToolbar();
26105         this.owner.deferFocus();
26106     },
26107
26108     /**
26109      * Executes a Midas editor command directly on the editor document.
26110      * For visual commands, you should use {@link #relayCmd} instead.
26111      * <b>This should only be called after the editor is initialized.</b>
26112      * @param {String} cmd The Midas command
26113      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26114      */
26115     execCmd : function(cmd, value){
26116         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26117         this.syncValue();
26118     },
26119  
26120  
26121    
26122     /**
26123      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26124      * to insert tRoo.
26125      * @param {String} text | dom node.. 
26126      */
26127     insertAtCursor : function(text)
26128     {
26129         
26130         if(!this.activated){
26131             return;
26132         }
26133         /*
26134         if(Roo.isIE){
26135             this.win.focus();
26136             var r = this.doc.selection.createRange();
26137             if(r){
26138                 r.collapse(true);
26139                 r.pasteHTML(text);
26140                 this.syncValue();
26141                 this.deferFocus();
26142             
26143             }
26144             return;
26145         }
26146         */
26147         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26148             this.win.focus();
26149             
26150             
26151             // from jquery ui (MIT licenced)
26152             var range, node;
26153             var win = this.win;
26154             
26155             if (win.getSelection && win.getSelection().getRangeAt) {
26156                 range = win.getSelection().getRangeAt(0);
26157                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26158                 range.insertNode(node);
26159             } else if (win.document.selection && win.document.selection.createRange) {
26160                 // no firefox support
26161                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26162                 win.document.selection.createRange().pasteHTML(txt);
26163             } else {
26164                 // no firefox support
26165                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26166                 this.execCmd('InsertHTML', txt);
26167             } 
26168             
26169             this.syncValue();
26170             
26171             this.deferFocus();
26172         }
26173     },
26174  // private
26175     mozKeyPress : function(e){
26176         if(e.ctrlKey){
26177             var c = e.getCharCode(), cmd;
26178           
26179             if(c > 0){
26180                 c = String.fromCharCode(c).toLowerCase();
26181                 switch(c){
26182                     case 'b':
26183                         cmd = 'bold';
26184                         break;
26185                     case 'i':
26186                         cmd = 'italic';
26187                         break;
26188                     
26189                     case 'u':
26190                         cmd = 'underline';
26191                         break;
26192                     
26193                     case 'v':
26194                         this.cleanUpPaste.defer(100, this);
26195                         return;
26196                         
26197                 }
26198                 if(cmd){
26199                     this.win.focus();
26200                     this.execCmd(cmd);
26201                     this.deferFocus();
26202                     e.preventDefault();
26203                 }
26204                 
26205             }
26206         }
26207     },
26208
26209     // private
26210     fixKeys : function(){ // load time branching for fastest keydown performance
26211         if(Roo.isIE){
26212             return function(e){
26213                 var k = e.getKey(), r;
26214                 if(k == e.TAB){
26215                     e.stopEvent();
26216                     r = this.doc.selection.createRange();
26217                     if(r){
26218                         r.collapse(true);
26219                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26220                         this.deferFocus();
26221                     }
26222                     return;
26223                 }
26224                 
26225                 if(k == e.ENTER){
26226                     r = this.doc.selection.createRange();
26227                     if(r){
26228                         var target = r.parentElement();
26229                         if(!target || target.tagName.toLowerCase() != 'li'){
26230                             e.stopEvent();
26231                             r.pasteHTML('<br />');
26232                             r.collapse(false);
26233                             r.select();
26234                         }
26235                     }
26236                 }
26237                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26238                     this.cleanUpPaste.defer(100, this);
26239                     return;
26240                 }
26241                 
26242                 
26243             };
26244         }else if(Roo.isOpera){
26245             return function(e){
26246                 var k = e.getKey();
26247                 if(k == e.TAB){
26248                     e.stopEvent();
26249                     this.win.focus();
26250                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26251                     this.deferFocus();
26252                 }
26253                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26254                     this.cleanUpPaste.defer(100, this);
26255                     return;
26256                 }
26257                 
26258             };
26259         }else if(Roo.isSafari){
26260             return function(e){
26261                 var k = e.getKey();
26262                 
26263                 if(k == e.TAB){
26264                     e.stopEvent();
26265                     this.execCmd('InsertText','\t');
26266                     this.deferFocus();
26267                     return;
26268                 }
26269                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26270                     this.cleanUpPaste.defer(100, this);
26271                     return;
26272                 }
26273                 
26274              };
26275         }
26276     }(),
26277     
26278     getAllAncestors: function()
26279     {
26280         var p = this.getSelectedNode();
26281         var a = [];
26282         if (!p) {
26283             a.push(p); // push blank onto stack..
26284             p = this.getParentElement();
26285         }
26286         
26287         
26288         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26289             a.push(p);
26290             p = p.parentNode;
26291         }
26292         a.push(this.doc.body);
26293         return a;
26294     },
26295     lastSel : false,
26296     lastSelNode : false,
26297     
26298     
26299     getSelection : function() 
26300     {
26301         this.assignDocWin();
26302         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26303     },
26304     
26305     getSelectedNode: function() 
26306     {
26307         // this may only work on Gecko!!!
26308         
26309         // should we cache this!!!!
26310         
26311         
26312         
26313          
26314         var range = this.createRange(this.getSelection()).cloneRange();
26315         
26316         if (Roo.isIE) {
26317             var parent = range.parentElement();
26318             while (true) {
26319                 var testRange = range.duplicate();
26320                 testRange.moveToElementText(parent);
26321                 if (testRange.inRange(range)) {
26322                     break;
26323                 }
26324                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26325                     break;
26326                 }
26327                 parent = parent.parentElement;
26328             }
26329             return parent;
26330         }
26331         
26332         // is ancestor a text element.
26333         var ac =  range.commonAncestorContainer;
26334         if (ac.nodeType == 3) {
26335             ac = ac.parentNode;
26336         }
26337         
26338         var ar = ac.childNodes;
26339          
26340         var nodes = [];
26341         var other_nodes = [];
26342         var has_other_nodes = false;
26343         for (var i=0;i<ar.length;i++) {
26344             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26345                 continue;
26346             }
26347             // fullly contained node.
26348             
26349             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26350                 nodes.push(ar[i]);
26351                 continue;
26352             }
26353             
26354             // probably selected..
26355             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26356                 other_nodes.push(ar[i]);
26357                 continue;
26358             }
26359             // outer..
26360             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26361                 continue;
26362             }
26363             
26364             
26365             has_other_nodes = true;
26366         }
26367         if (!nodes.length && other_nodes.length) {
26368             nodes= other_nodes;
26369         }
26370         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26371             return false;
26372         }
26373         
26374         return nodes[0];
26375     },
26376     createRange: function(sel)
26377     {
26378         // this has strange effects when using with 
26379         // top toolbar - not sure if it's a great idea.
26380         //this.editor.contentWindow.focus();
26381         if (typeof sel != "undefined") {
26382             try {
26383                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26384             } catch(e) {
26385                 return this.doc.createRange();
26386             }
26387         } else {
26388             return this.doc.createRange();
26389         }
26390     },
26391     getParentElement: function()
26392     {
26393         
26394         this.assignDocWin();
26395         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26396         
26397         var range = this.createRange(sel);
26398          
26399         try {
26400             var p = range.commonAncestorContainer;
26401             while (p.nodeType == 3) { // text node
26402                 p = p.parentNode;
26403             }
26404             return p;
26405         } catch (e) {
26406             return null;
26407         }
26408     
26409     },
26410     /***
26411      *
26412      * Range intersection.. the hard stuff...
26413      *  '-1' = before
26414      *  '0' = hits..
26415      *  '1' = after.
26416      *         [ -- selected range --- ]
26417      *   [fail]                        [fail]
26418      *
26419      *    basically..
26420      *      if end is before start or  hits it. fail.
26421      *      if start is after end or hits it fail.
26422      *
26423      *   if either hits (but other is outside. - then it's not 
26424      *   
26425      *    
26426      **/
26427     
26428     
26429     // @see http://www.thismuchiknow.co.uk/?p=64.
26430     rangeIntersectsNode : function(range, node)
26431     {
26432         var nodeRange = node.ownerDocument.createRange();
26433         try {
26434             nodeRange.selectNode(node);
26435         } catch (e) {
26436             nodeRange.selectNodeContents(node);
26437         }
26438     
26439         var rangeStartRange = range.cloneRange();
26440         rangeStartRange.collapse(true);
26441     
26442         var rangeEndRange = range.cloneRange();
26443         rangeEndRange.collapse(false);
26444     
26445         var nodeStartRange = nodeRange.cloneRange();
26446         nodeStartRange.collapse(true);
26447     
26448         var nodeEndRange = nodeRange.cloneRange();
26449         nodeEndRange.collapse(false);
26450     
26451         return rangeStartRange.compareBoundaryPoints(
26452                  Range.START_TO_START, nodeEndRange) == -1 &&
26453                rangeEndRange.compareBoundaryPoints(
26454                  Range.START_TO_START, nodeStartRange) == 1;
26455         
26456          
26457     },
26458     rangeCompareNode : function(range, node)
26459     {
26460         var nodeRange = node.ownerDocument.createRange();
26461         try {
26462             nodeRange.selectNode(node);
26463         } catch (e) {
26464             nodeRange.selectNodeContents(node);
26465         }
26466         
26467         
26468         range.collapse(true);
26469     
26470         nodeRange.collapse(true);
26471      
26472         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26473         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26474          
26475         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26476         
26477         var nodeIsBefore   =  ss == 1;
26478         var nodeIsAfter    = ee == -1;
26479         
26480         if (nodeIsBefore && nodeIsAfter) {
26481             return 0; // outer
26482         }
26483         if (!nodeIsBefore && nodeIsAfter) {
26484             return 1; //right trailed.
26485         }
26486         
26487         if (nodeIsBefore && !nodeIsAfter) {
26488             return 2;  // left trailed.
26489         }
26490         // fully contined.
26491         return 3;
26492     },
26493
26494     // private? - in a new class?
26495     cleanUpPaste :  function()
26496     {
26497         // cleans up the whole document..
26498         Roo.log('cleanuppaste');
26499         
26500         this.cleanUpChildren(this.doc.body);
26501         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26502         if (clean != this.doc.body.innerHTML) {
26503             this.doc.body.innerHTML = clean;
26504         }
26505         
26506     },
26507     
26508     cleanWordChars : function(input) {// change the chars to hex code
26509         var he = Roo.HtmlEditorCore;
26510         
26511         var output = input;
26512         Roo.each(he.swapCodes, function(sw) { 
26513             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26514             
26515             output = output.replace(swapper, sw[1]);
26516         });
26517         
26518         return output;
26519     },
26520     
26521     
26522     cleanUpChildren : function (n)
26523     {
26524         if (!n.childNodes.length) {
26525             return;
26526         }
26527         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26528            this.cleanUpChild(n.childNodes[i]);
26529         }
26530     },
26531     
26532     
26533         
26534     
26535     cleanUpChild : function (node)
26536     {
26537         var ed = this;
26538         //console.log(node);
26539         if (node.nodeName == "#text") {
26540             // clean up silly Windows -- stuff?
26541             return; 
26542         }
26543         if (node.nodeName == "#comment") {
26544             if (!this.allowComments) {
26545                 node.parentNode.removeChild(node);
26546             }
26547             // clean up silly Windows -- stuff?
26548             return; 
26549         }
26550         var lcname = node.tagName.toLowerCase();
26551         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26552         // whitelist of tags..
26553         
26554         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26555             // remove node.
26556             node.parentNode.removeChild(node);
26557             return;
26558             
26559         }
26560         
26561         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26562         
26563         // spans with no attributes - just remove them..
26564         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26565             remove_keep_children = true;
26566         }
26567         
26568         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26569         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26570         
26571         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26572         //    remove_keep_children = true;
26573         //}
26574         
26575         if (remove_keep_children) {
26576             this.cleanUpChildren(node);
26577             // inserts everything just before this node...
26578             while (node.childNodes.length) {
26579                 var cn = node.childNodes[0];
26580                 node.removeChild(cn);
26581                 node.parentNode.insertBefore(cn, node);
26582             }
26583             node.parentNode.removeChild(node);
26584             return;
26585         }
26586         
26587         if (!node.attributes || !node.attributes.length) {
26588             
26589           
26590             
26591             
26592             this.cleanUpChildren(node);
26593             return;
26594         }
26595         
26596         function cleanAttr(n,v)
26597         {
26598             
26599             if (v.match(/^\./) || v.match(/^\//)) {
26600                 return;
26601             }
26602             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26603                 return;
26604             }
26605             if (v.match(/^#/)) {
26606                 return;
26607             }
26608             if (v.match(/^\{/)) { // allow template editing.
26609                 return;
26610             }
26611 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26612             node.removeAttribute(n);
26613             
26614         }
26615         
26616         var cwhite = this.cwhite;
26617         var cblack = this.cblack;
26618             
26619         function cleanStyle(n,v)
26620         {
26621             if (v.match(/expression/)) { //XSS?? should we even bother..
26622                 node.removeAttribute(n);
26623                 return;
26624             }
26625             
26626             var parts = v.split(/;/);
26627             var clean = [];
26628             
26629             Roo.each(parts, function(p) {
26630                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26631                 if (!p.length) {
26632                     return true;
26633                 }
26634                 var l = p.split(':').shift().replace(/\s+/g,'');
26635                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26636                 
26637                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26638 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26639                     //node.removeAttribute(n);
26640                     return true;
26641                 }
26642                 //Roo.log()
26643                 // only allow 'c whitelisted system attributes'
26644                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26645 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26646                     //node.removeAttribute(n);
26647                     return true;
26648                 }
26649                 
26650                 
26651                  
26652                 
26653                 clean.push(p);
26654                 return true;
26655             });
26656             if (clean.length) { 
26657                 node.setAttribute(n, clean.join(';'));
26658             } else {
26659                 node.removeAttribute(n);
26660             }
26661             
26662         }
26663         
26664         
26665         for (var i = node.attributes.length-1; i > -1 ; i--) {
26666             var a = node.attributes[i];
26667             //console.log(a);
26668             
26669             if (a.name.toLowerCase().substr(0,2)=='on')  {
26670                 node.removeAttribute(a.name);
26671                 continue;
26672             }
26673             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26674                 node.removeAttribute(a.name);
26675                 continue;
26676             }
26677             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26678                 cleanAttr(a.name,a.value); // fixme..
26679                 continue;
26680             }
26681             if (a.name == 'style') {
26682                 cleanStyle(a.name,a.value);
26683                 continue;
26684             }
26685             /// clean up MS crap..
26686             // tecnically this should be a list of valid class'es..
26687             
26688             
26689             if (a.name == 'class') {
26690                 if (a.value.match(/^Mso/)) {
26691                     node.removeAttribute('class');
26692                 }
26693                 
26694                 if (a.value.match(/^body$/)) {
26695                     node.removeAttribute('class');
26696                 }
26697                 continue;
26698             }
26699             
26700             // style cleanup!?
26701             // class cleanup?
26702             
26703         }
26704         
26705         
26706         this.cleanUpChildren(node);
26707         
26708         
26709     },
26710     
26711     /**
26712      * Clean up MS wordisms...
26713      */
26714     cleanWord : function(node)
26715     {
26716         if (!node) {
26717             this.cleanWord(this.doc.body);
26718             return;
26719         }
26720         
26721         if(
26722                 node.nodeName == 'SPAN' &&
26723                 !node.hasAttributes() &&
26724                 node.childNodes.length == 1 &&
26725                 node.firstChild.nodeName == "#text"  
26726         ) {
26727             var textNode = node.firstChild;
26728             node.removeChild(textNode);
26729             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26730                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26731             }
26732             node.parentNode.insertBefore(textNode, node);
26733             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26734                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26735             }
26736             node.parentNode.removeChild(node);
26737         }
26738         
26739         if (node.nodeName == "#text") {
26740             // clean up silly Windows -- stuff?
26741             return; 
26742         }
26743         if (node.nodeName == "#comment") {
26744             node.parentNode.removeChild(node);
26745             // clean up silly Windows -- stuff?
26746             return; 
26747         }
26748         
26749         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26750             node.parentNode.removeChild(node);
26751             return;
26752         }
26753         //Roo.log(node.tagName);
26754         // remove - but keep children..
26755         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26756             //Roo.log('-- removed');
26757             while (node.childNodes.length) {
26758                 var cn = node.childNodes[0];
26759                 node.removeChild(cn);
26760                 node.parentNode.insertBefore(cn, node);
26761                 // move node to parent - and clean it..
26762                 this.cleanWord(cn);
26763             }
26764             node.parentNode.removeChild(node);
26765             /// no need to iterate chidlren = it's got none..
26766             //this.iterateChildren(node, this.cleanWord);
26767             return;
26768         }
26769         // clean styles
26770         if (node.className.length) {
26771             
26772             var cn = node.className.split(/\W+/);
26773             var cna = [];
26774             Roo.each(cn, function(cls) {
26775                 if (cls.match(/Mso[a-zA-Z]+/)) {
26776                     return;
26777                 }
26778                 cna.push(cls);
26779             });
26780             node.className = cna.length ? cna.join(' ') : '';
26781             if (!cna.length) {
26782                 node.removeAttribute("class");
26783             }
26784         }
26785         
26786         if (node.hasAttribute("lang")) {
26787             node.removeAttribute("lang");
26788         }
26789         
26790         if (node.hasAttribute("style")) {
26791             
26792             var styles = node.getAttribute("style").split(";");
26793             var nstyle = [];
26794             Roo.each(styles, function(s) {
26795                 if (!s.match(/:/)) {
26796                     return;
26797                 }
26798                 var kv = s.split(":");
26799                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26800                     return;
26801                 }
26802                 // what ever is left... we allow.
26803                 nstyle.push(s);
26804             });
26805             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26806             if (!nstyle.length) {
26807                 node.removeAttribute('style');
26808             }
26809         }
26810         this.iterateChildren(node, this.cleanWord);
26811         
26812         
26813         
26814     },
26815     /**
26816      * iterateChildren of a Node, calling fn each time, using this as the scole..
26817      * @param {DomNode} node node to iterate children of.
26818      * @param {Function} fn method of this class to call on each item.
26819      */
26820     iterateChildren : function(node, fn)
26821     {
26822         if (!node.childNodes.length) {
26823                 return;
26824         }
26825         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26826            fn.call(this, node.childNodes[i])
26827         }
26828     },
26829     
26830     
26831     /**
26832      * cleanTableWidths.
26833      *
26834      * Quite often pasting from word etc.. results in tables with column and widths.
26835      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26836      *
26837      */
26838     cleanTableWidths : function(node)
26839     {
26840          
26841          
26842         if (!node) {
26843             this.cleanTableWidths(this.doc.body);
26844             return;
26845         }
26846         
26847         // ignore list...
26848         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26849             return; 
26850         }
26851         Roo.log(node.tagName);
26852         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26853             this.iterateChildren(node, this.cleanTableWidths);
26854             return;
26855         }
26856         if (node.hasAttribute('width')) {
26857             node.removeAttribute('width');
26858         }
26859         
26860          
26861         if (node.hasAttribute("style")) {
26862             // pretty basic...
26863             
26864             var styles = node.getAttribute("style").split(";");
26865             var nstyle = [];
26866             Roo.each(styles, function(s) {
26867                 if (!s.match(/:/)) {
26868                     return;
26869                 }
26870                 var kv = s.split(":");
26871                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26872                     return;
26873                 }
26874                 // what ever is left... we allow.
26875                 nstyle.push(s);
26876             });
26877             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26878             if (!nstyle.length) {
26879                 node.removeAttribute('style');
26880             }
26881         }
26882         
26883         this.iterateChildren(node, this.cleanTableWidths);
26884         
26885         
26886     },
26887     
26888     
26889     
26890     
26891     domToHTML : function(currentElement, depth, nopadtext) {
26892         
26893         depth = depth || 0;
26894         nopadtext = nopadtext || false;
26895     
26896         if (!currentElement) {
26897             return this.domToHTML(this.doc.body);
26898         }
26899         
26900         //Roo.log(currentElement);
26901         var j;
26902         var allText = false;
26903         var nodeName = currentElement.nodeName;
26904         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26905         
26906         if  (nodeName == '#text') {
26907             
26908             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26909         }
26910         
26911         
26912         var ret = '';
26913         if (nodeName != 'BODY') {
26914              
26915             var i = 0;
26916             // Prints the node tagName, such as <A>, <IMG>, etc
26917             if (tagName) {
26918                 var attr = [];
26919                 for(i = 0; i < currentElement.attributes.length;i++) {
26920                     // quoting?
26921                     var aname = currentElement.attributes.item(i).name;
26922                     if (!currentElement.attributes.item(i).value.length) {
26923                         continue;
26924                     }
26925                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26926                 }
26927                 
26928                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26929             } 
26930             else {
26931                 
26932                 // eack
26933             }
26934         } else {
26935             tagName = false;
26936         }
26937         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26938             return ret;
26939         }
26940         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26941             nopadtext = true;
26942         }
26943         
26944         
26945         // Traverse the tree
26946         i = 0;
26947         var currentElementChild = currentElement.childNodes.item(i);
26948         var allText = true;
26949         var innerHTML  = '';
26950         lastnode = '';
26951         while (currentElementChild) {
26952             // Formatting code (indent the tree so it looks nice on the screen)
26953             var nopad = nopadtext;
26954             if (lastnode == 'SPAN') {
26955                 nopad  = true;
26956             }
26957             // text
26958             if  (currentElementChild.nodeName == '#text') {
26959                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26960                 toadd = nopadtext ? toadd : toadd.trim();
26961                 if (!nopad && toadd.length > 80) {
26962                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26963                 }
26964                 innerHTML  += toadd;
26965                 
26966                 i++;
26967                 currentElementChild = currentElement.childNodes.item(i);
26968                 lastNode = '';
26969                 continue;
26970             }
26971             allText = false;
26972             
26973             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26974                 
26975             // Recursively traverse the tree structure of the child node
26976             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26977             lastnode = currentElementChild.nodeName;
26978             i++;
26979             currentElementChild=currentElement.childNodes.item(i);
26980         }
26981         
26982         ret += innerHTML;
26983         
26984         if (!allText) {
26985                 // The remaining code is mostly for formatting the tree
26986             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26987         }
26988         
26989         
26990         if (tagName) {
26991             ret+= "</"+tagName+">";
26992         }
26993         return ret;
26994         
26995     },
26996         
26997     applyBlacklists : function()
26998     {
26999         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27000         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27001         
27002         this.white = [];
27003         this.black = [];
27004         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27005             if (b.indexOf(tag) > -1) {
27006                 return;
27007             }
27008             this.white.push(tag);
27009             
27010         }, this);
27011         
27012         Roo.each(w, function(tag) {
27013             if (b.indexOf(tag) > -1) {
27014                 return;
27015             }
27016             if (this.white.indexOf(tag) > -1) {
27017                 return;
27018             }
27019             this.white.push(tag);
27020             
27021         }, this);
27022         
27023         
27024         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27025             if (w.indexOf(tag) > -1) {
27026                 return;
27027             }
27028             this.black.push(tag);
27029             
27030         }, this);
27031         
27032         Roo.each(b, function(tag) {
27033             if (w.indexOf(tag) > -1) {
27034                 return;
27035             }
27036             if (this.black.indexOf(tag) > -1) {
27037                 return;
27038             }
27039             this.black.push(tag);
27040             
27041         }, this);
27042         
27043         
27044         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27045         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27046         
27047         this.cwhite = [];
27048         this.cblack = [];
27049         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27050             if (b.indexOf(tag) > -1) {
27051                 return;
27052             }
27053             this.cwhite.push(tag);
27054             
27055         }, this);
27056         
27057         Roo.each(w, function(tag) {
27058             if (b.indexOf(tag) > -1) {
27059                 return;
27060             }
27061             if (this.cwhite.indexOf(tag) > -1) {
27062                 return;
27063             }
27064             this.cwhite.push(tag);
27065             
27066         }, this);
27067         
27068         
27069         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27070             if (w.indexOf(tag) > -1) {
27071                 return;
27072             }
27073             this.cblack.push(tag);
27074             
27075         }, this);
27076         
27077         Roo.each(b, function(tag) {
27078             if (w.indexOf(tag) > -1) {
27079                 return;
27080             }
27081             if (this.cblack.indexOf(tag) > -1) {
27082                 return;
27083             }
27084             this.cblack.push(tag);
27085             
27086         }, this);
27087     },
27088     
27089     setStylesheets : function(stylesheets)
27090     {
27091         if(typeof(stylesheets) == 'string'){
27092             Roo.get(this.iframe.contentDocument.head).createChild({
27093                 tag : 'link',
27094                 rel : 'stylesheet',
27095                 type : 'text/css',
27096                 href : stylesheets
27097             });
27098             
27099             return;
27100         }
27101         var _this = this;
27102      
27103         Roo.each(stylesheets, function(s) {
27104             if(!s.length){
27105                 return;
27106             }
27107             
27108             Roo.get(_this.iframe.contentDocument.head).createChild({
27109                 tag : 'link',
27110                 rel : 'stylesheet',
27111                 type : 'text/css',
27112                 href : s
27113             });
27114         });
27115
27116         
27117     },
27118     
27119     removeStylesheets : function()
27120     {
27121         var _this = this;
27122         
27123         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27124             s.remove();
27125         });
27126     },
27127     
27128     setStyle : function(style)
27129     {
27130         Roo.get(this.iframe.contentDocument.head).createChild({
27131             tag : 'style',
27132             type : 'text/css',
27133             html : style
27134         });
27135
27136         return;
27137     }
27138     
27139     // hide stuff that is not compatible
27140     /**
27141      * @event blur
27142      * @hide
27143      */
27144     /**
27145      * @event change
27146      * @hide
27147      */
27148     /**
27149      * @event focus
27150      * @hide
27151      */
27152     /**
27153      * @event specialkey
27154      * @hide
27155      */
27156     /**
27157      * @cfg {String} fieldClass @hide
27158      */
27159     /**
27160      * @cfg {String} focusClass @hide
27161      */
27162     /**
27163      * @cfg {String} autoCreate @hide
27164      */
27165     /**
27166      * @cfg {String} inputType @hide
27167      */
27168     /**
27169      * @cfg {String} invalidClass @hide
27170      */
27171     /**
27172      * @cfg {String} invalidText @hide
27173      */
27174     /**
27175      * @cfg {String} msgFx @hide
27176      */
27177     /**
27178      * @cfg {String} validateOnBlur @hide
27179      */
27180 });
27181
27182 Roo.HtmlEditorCore.white = [
27183         'area', 'br', 'img', 'input', 'hr', 'wbr',
27184         
27185        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27186        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27187        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27188        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27189        'table',   'ul',         'xmp', 
27190        
27191        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27192       'thead',   'tr', 
27193      
27194       'dir', 'menu', 'ol', 'ul', 'dl',
27195        
27196       'embed',  'object'
27197 ];
27198
27199
27200 Roo.HtmlEditorCore.black = [
27201     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27202         'applet', // 
27203         'base',   'basefont', 'bgsound', 'blink',  'body', 
27204         'frame',  'frameset', 'head',    'html',   'ilayer', 
27205         'iframe', 'layer',  'link',     'meta',    'object',   
27206         'script', 'style' ,'title',  'xml' // clean later..
27207 ];
27208 Roo.HtmlEditorCore.clean = [
27209     'script', 'style', 'title', 'xml'
27210 ];
27211 Roo.HtmlEditorCore.remove = [
27212     'font'
27213 ];
27214 // attributes..
27215
27216 Roo.HtmlEditorCore.ablack = [
27217     'on'
27218 ];
27219     
27220 Roo.HtmlEditorCore.aclean = [ 
27221     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27222 ];
27223
27224 // protocols..
27225 Roo.HtmlEditorCore.pwhite= [
27226         'http',  'https',  'mailto'
27227 ];
27228
27229 // white listed style attributes.
27230 Roo.HtmlEditorCore.cwhite= [
27231       //  'text-align', /// default is to allow most things..
27232       
27233          
27234 //        'font-size'//??
27235 ];
27236
27237 // black listed style attributes.
27238 Roo.HtmlEditorCore.cblack= [
27239       //  'font-size' -- this can be set by the project 
27240 ];
27241
27242
27243 Roo.HtmlEditorCore.swapCodes   =[ 
27244     [    8211, "&#8211;" ], 
27245     [    8212, "&#8212;" ], 
27246     [    8216,  "'" ],  
27247     [    8217, "'" ],  
27248     [    8220, '"' ],  
27249     [    8221, '"' ],  
27250     [    8226, "*" ],  
27251     [    8230, "..." ]
27252 ]; 
27253
27254     /*
27255  * - LGPL
27256  *
27257  * HtmlEditor
27258  * 
27259  */
27260
27261 /**
27262  * @class Roo.bootstrap.HtmlEditor
27263  * @extends Roo.bootstrap.TextArea
27264  * Bootstrap HtmlEditor class
27265
27266  * @constructor
27267  * Create a new HtmlEditor
27268  * @param {Object} config The config object
27269  */
27270
27271 Roo.bootstrap.HtmlEditor = function(config){
27272     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27273     if (!this.toolbars) {
27274         this.toolbars = [];
27275     }
27276     
27277     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27278     this.addEvents({
27279             /**
27280              * @event initialize
27281              * Fires when the editor is fully initialized (including the iframe)
27282              * @param {HtmlEditor} this
27283              */
27284             initialize: true,
27285             /**
27286              * @event activate
27287              * Fires when the editor is first receives the focus. Any insertion must wait
27288              * until after this event.
27289              * @param {HtmlEditor} this
27290              */
27291             activate: true,
27292              /**
27293              * @event beforesync
27294              * Fires before the textarea is updated with content from the editor iframe. Return false
27295              * to cancel the sync.
27296              * @param {HtmlEditor} this
27297              * @param {String} html
27298              */
27299             beforesync: true,
27300              /**
27301              * @event beforepush
27302              * Fires before the iframe editor is updated with content from the textarea. Return false
27303              * to cancel the push.
27304              * @param {HtmlEditor} this
27305              * @param {String} html
27306              */
27307             beforepush: true,
27308              /**
27309              * @event sync
27310              * Fires when the textarea is updated with content from the editor iframe.
27311              * @param {HtmlEditor} this
27312              * @param {String} html
27313              */
27314             sync: true,
27315              /**
27316              * @event push
27317              * Fires when the iframe editor is updated with content from the textarea.
27318              * @param {HtmlEditor} this
27319              * @param {String} html
27320              */
27321             push: true,
27322              /**
27323              * @event editmodechange
27324              * Fires when the editor switches edit modes
27325              * @param {HtmlEditor} this
27326              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27327              */
27328             editmodechange: true,
27329             /**
27330              * @event editorevent
27331              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27332              * @param {HtmlEditor} this
27333              */
27334             editorevent: true,
27335             /**
27336              * @event firstfocus
27337              * Fires when on first focus - needed by toolbars..
27338              * @param {HtmlEditor} this
27339              */
27340             firstfocus: true,
27341             /**
27342              * @event autosave
27343              * Auto save the htmlEditor value as a file into Events
27344              * @param {HtmlEditor} this
27345              */
27346             autosave: true,
27347             /**
27348              * @event savedpreview
27349              * preview the saved version of htmlEditor
27350              * @param {HtmlEditor} this
27351              */
27352             savedpreview: true
27353         });
27354 };
27355
27356
27357 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27358     
27359     
27360       /**
27361      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27362      */
27363     toolbars : false,
27364     
27365      /**
27366     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27367     */
27368     btns : [],
27369    
27370      /**
27371      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27372      *                        Roo.resizable.
27373      */
27374     resizable : false,
27375      /**
27376      * @cfg {Number} height (in pixels)
27377      */   
27378     height: 300,
27379    /**
27380      * @cfg {Number} width (in pixels)
27381      */   
27382     width: false,
27383     
27384     /**
27385      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27386      * 
27387      */
27388     stylesheets: false,
27389     
27390     // id of frame..
27391     frameId: false,
27392     
27393     // private properties
27394     validationEvent : false,
27395     deferHeight: true,
27396     initialized : false,
27397     activated : false,
27398     
27399     onFocus : Roo.emptyFn,
27400     iframePad:3,
27401     hideMode:'offsets',
27402     
27403     tbContainer : false,
27404     
27405     bodyCls : '',
27406     
27407     toolbarContainer :function() {
27408         return this.wrap.select('.x-html-editor-tb',true).first();
27409     },
27410
27411     /**
27412      * Protected method that will not generally be called directly. It
27413      * is called when the editor creates its toolbar. Override this method if you need to
27414      * add custom toolbar buttons.
27415      * @param {HtmlEditor} editor
27416      */
27417     createToolbar : function(){
27418         Roo.log('renewing');
27419         Roo.log("create toolbars");
27420         
27421         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27422         this.toolbars[0].render(this.toolbarContainer());
27423         
27424         return;
27425         
27426 //        if (!editor.toolbars || !editor.toolbars.length) {
27427 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27428 //        }
27429 //        
27430 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27431 //            editor.toolbars[i] = Roo.factory(
27432 //                    typeof(editor.toolbars[i]) == 'string' ?
27433 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27434 //                Roo.bootstrap.HtmlEditor);
27435 //            editor.toolbars[i].init(editor);
27436 //        }
27437     },
27438
27439      
27440     // private
27441     onRender : function(ct, position)
27442     {
27443        // Roo.log("Call onRender: " + this.xtype);
27444         var _t = this;
27445         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27446       
27447         this.wrap = this.inputEl().wrap({
27448             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27449         });
27450         
27451         this.editorcore.onRender(ct, position);
27452          
27453         if (this.resizable) {
27454             this.resizeEl = new Roo.Resizable(this.wrap, {
27455                 pinned : true,
27456                 wrap: true,
27457                 dynamic : true,
27458                 minHeight : this.height,
27459                 height: this.height,
27460                 handles : this.resizable,
27461                 width: this.width,
27462                 listeners : {
27463                     resize : function(r, w, h) {
27464                         _t.onResize(w,h); // -something
27465                     }
27466                 }
27467             });
27468             
27469         }
27470         this.createToolbar(this);
27471        
27472         
27473         if(!this.width && this.resizable){
27474             this.setSize(this.wrap.getSize());
27475         }
27476         if (this.resizeEl) {
27477             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27478             // should trigger onReize..
27479         }
27480         
27481     },
27482
27483     // private
27484     onResize : function(w, h)
27485     {
27486         Roo.log('resize: ' +w + ',' + h );
27487         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27488         var ew = false;
27489         var eh = false;
27490         
27491         if(this.inputEl() ){
27492             if(typeof w == 'number'){
27493                 var aw = w - this.wrap.getFrameWidth('lr');
27494                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27495                 ew = aw;
27496             }
27497             if(typeof h == 'number'){
27498                  var tbh = -11;  // fixme it needs to tool bar size!
27499                 for (var i =0; i < this.toolbars.length;i++) {
27500                     // fixme - ask toolbars for heights?
27501                     tbh += this.toolbars[i].el.getHeight();
27502                     //if (this.toolbars[i].footer) {
27503                     //    tbh += this.toolbars[i].footer.el.getHeight();
27504                     //}
27505                 }
27506               
27507                 
27508                 
27509                 
27510                 
27511                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27512                 ah -= 5; // knock a few pixes off for look..
27513                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27514                 var eh = ah;
27515             }
27516         }
27517         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27518         this.editorcore.onResize(ew,eh);
27519         
27520     },
27521
27522     /**
27523      * Toggles the editor between standard and source edit mode.
27524      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27525      */
27526     toggleSourceEdit : function(sourceEditMode)
27527     {
27528         this.editorcore.toggleSourceEdit(sourceEditMode);
27529         
27530         if(this.editorcore.sourceEditMode){
27531             Roo.log('editor - showing textarea');
27532             
27533 //            Roo.log('in');
27534 //            Roo.log(this.syncValue());
27535             this.syncValue();
27536             this.inputEl().removeClass(['hide', 'x-hidden']);
27537             this.inputEl().dom.removeAttribute('tabIndex');
27538             this.inputEl().focus();
27539         }else{
27540             Roo.log('editor - hiding textarea');
27541 //            Roo.log('out')
27542 //            Roo.log(this.pushValue()); 
27543             this.pushValue();
27544             
27545             this.inputEl().addClass(['hide', 'x-hidden']);
27546             this.inputEl().dom.setAttribute('tabIndex', -1);
27547             //this.deferFocus();
27548         }
27549          
27550         if(this.resizable){
27551             this.setSize(this.wrap.getSize());
27552         }
27553         
27554         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27555     },
27556  
27557     // private (for BoxComponent)
27558     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27559
27560     // private (for BoxComponent)
27561     getResizeEl : function(){
27562         return this.wrap;
27563     },
27564
27565     // private (for BoxComponent)
27566     getPositionEl : function(){
27567         return this.wrap;
27568     },
27569
27570     // private
27571     initEvents : function(){
27572         this.originalValue = this.getValue();
27573     },
27574
27575 //    /**
27576 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27577 //     * @method
27578 //     */
27579 //    markInvalid : Roo.emptyFn,
27580 //    /**
27581 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27582 //     * @method
27583 //     */
27584 //    clearInvalid : Roo.emptyFn,
27585
27586     setValue : function(v){
27587         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27588         this.editorcore.pushValue();
27589     },
27590
27591      
27592     // private
27593     deferFocus : function(){
27594         this.focus.defer(10, this);
27595     },
27596
27597     // doc'ed in Field
27598     focus : function(){
27599         this.editorcore.focus();
27600         
27601     },
27602       
27603
27604     // private
27605     onDestroy : function(){
27606         
27607         
27608         
27609         if(this.rendered){
27610             
27611             for (var i =0; i < this.toolbars.length;i++) {
27612                 // fixme - ask toolbars for heights?
27613                 this.toolbars[i].onDestroy();
27614             }
27615             
27616             this.wrap.dom.innerHTML = '';
27617             this.wrap.remove();
27618         }
27619     },
27620
27621     // private
27622     onFirstFocus : function(){
27623         //Roo.log("onFirstFocus");
27624         this.editorcore.onFirstFocus();
27625          for (var i =0; i < this.toolbars.length;i++) {
27626             this.toolbars[i].onFirstFocus();
27627         }
27628         
27629     },
27630     
27631     // private
27632     syncValue : function()
27633     {   
27634         this.editorcore.syncValue();
27635     },
27636     
27637     pushValue : function()
27638     {   
27639         this.editorcore.pushValue();
27640     }
27641      
27642     
27643     // hide stuff that is not compatible
27644     /**
27645      * @event blur
27646      * @hide
27647      */
27648     /**
27649      * @event change
27650      * @hide
27651      */
27652     /**
27653      * @event focus
27654      * @hide
27655      */
27656     /**
27657      * @event specialkey
27658      * @hide
27659      */
27660     /**
27661      * @cfg {String} fieldClass @hide
27662      */
27663     /**
27664      * @cfg {String} focusClass @hide
27665      */
27666     /**
27667      * @cfg {String} autoCreate @hide
27668      */
27669     /**
27670      * @cfg {String} inputType @hide
27671      */
27672      
27673     /**
27674      * @cfg {String} invalidText @hide
27675      */
27676     /**
27677      * @cfg {String} msgFx @hide
27678      */
27679     /**
27680      * @cfg {String} validateOnBlur @hide
27681      */
27682 });
27683  
27684     
27685    
27686    
27687    
27688       
27689 Roo.namespace('Roo.bootstrap.htmleditor');
27690 /**
27691  * @class Roo.bootstrap.HtmlEditorToolbar1
27692  * Basic Toolbar
27693  * 
27694  * @example
27695  * Usage:
27696  *
27697  new Roo.bootstrap.HtmlEditor({
27698     ....
27699     toolbars : [
27700         new Roo.bootstrap.HtmlEditorToolbar1({
27701             disable : { fonts: 1 , format: 1, ..., ... , ...],
27702             btns : [ .... ]
27703         })
27704     }
27705      
27706  * 
27707  * @cfg {Object} disable List of elements to disable..
27708  * @cfg {Array} btns List of additional buttons.
27709  * 
27710  * 
27711  * NEEDS Extra CSS? 
27712  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27713  */
27714  
27715 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27716 {
27717     
27718     Roo.apply(this, config);
27719     
27720     // default disabled, based on 'good practice'..
27721     this.disable = this.disable || {};
27722     Roo.applyIf(this.disable, {
27723         fontSize : true,
27724         colors : true,
27725         specialElements : true
27726     });
27727     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27728     
27729     this.editor = config.editor;
27730     this.editorcore = config.editor.editorcore;
27731     
27732     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27733     
27734     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27735     // dont call parent... till later.
27736 }
27737 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27738      
27739     bar : true,
27740     
27741     editor : false,
27742     editorcore : false,
27743     
27744     
27745     formats : [
27746         "p" ,  
27747         "h1","h2","h3","h4","h5","h6", 
27748         "pre", "code", 
27749         "abbr", "acronym", "address", "cite", "samp", "var",
27750         'div','span'
27751     ],
27752     
27753     onRender : function(ct, position)
27754     {
27755        // Roo.log("Call onRender: " + this.xtype);
27756         
27757        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27758        Roo.log(this.el);
27759        this.el.dom.style.marginBottom = '0';
27760        var _this = this;
27761        var editorcore = this.editorcore;
27762        var editor= this.editor;
27763        
27764        var children = [];
27765        var btn = function(id,cmd , toggle, handler, html){
27766        
27767             var  event = toggle ? 'toggle' : 'click';
27768        
27769             var a = {
27770                 size : 'sm',
27771                 xtype: 'Button',
27772                 xns: Roo.bootstrap,
27773                 //glyphicon : id,
27774                 fa: id,
27775                 cmd : id || cmd,
27776                 enableToggle:toggle !== false,
27777                 html : html || '',
27778                 pressed : toggle ? false : null,
27779                 listeners : {}
27780             };
27781             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27782                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27783             };
27784             children.push(a);
27785             return a;
27786        }
27787        
27788     //    var cb_box = function...
27789         
27790         var style = {
27791                 xtype: 'Button',
27792                 size : 'sm',
27793                 xns: Roo.bootstrap,
27794                 fa : 'font',
27795                 //html : 'submit'
27796                 menu : {
27797                     xtype: 'Menu',
27798                     xns: Roo.bootstrap,
27799                     items:  []
27800                 }
27801         };
27802         Roo.each(this.formats, function(f) {
27803             style.menu.items.push({
27804                 xtype :'MenuItem',
27805                 xns: Roo.bootstrap,
27806                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27807                 tagname : f,
27808                 listeners : {
27809                     click : function()
27810                     {
27811                         editorcore.insertTag(this.tagname);
27812                         editor.focus();
27813                     }
27814                 }
27815                 
27816             });
27817         });
27818         children.push(style);   
27819         
27820         btn('bold',false,true);
27821         btn('italic',false,true);
27822         btn('align-left', 'justifyleft',true);
27823         btn('align-center', 'justifycenter',true);
27824         btn('align-right' , 'justifyright',true);
27825         btn('link', false, false, function(btn) {
27826             //Roo.log("create link?");
27827             var url = prompt(this.createLinkText, this.defaultLinkValue);
27828             if(url && url != 'http:/'+'/'){
27829                 this.editorcore.relayCmd('createlink', url);
27830             }
27831         }),
27832         btn('list','insertunorderedlist',true);
27833         btn('pencil', false,true, function(btn){
27834                 Roo.log(this);
27835                 this.toggleSourceEdit(btn.pressed);
27836         });
27837         
27838         if (this.editor.btns.length > 0) {
27839             for (var i = 0; i<this.editor.btns.length; i++) {
27840                 children.push(this.editor.btns[i]);
27841             }
27842         }
27843         
27844         /*
27845         var cog = {
27846                 xtype: 'Button',
27847                 size : 'sm',
27848                 xns: Roo.bootstrap,
27849                 glyphicon : 'cog',
27850                 //html : 'submit'
27851                 menu : {
27852                     xtype: 'Menu',
27853                     xns: Roo.bootstrap,
27854                     items:  []
27855                 }
27856         };
27857         
27858         cog.menu.items.push({
27859             xtype :'MenuItem',
27860             xns: Roo.bootstrap,
27861             html : Clean styles,
27862             tagname : f,
27863             listeners : {
27864                 click : function()
27865                 {
27866                     editorcore.insertTag(this.tagname);
27867                     editor.focus();
27868                 }
27869             }
27870             
27871         });
27872        */
27873         
27874          
27875        this.xtype = 'NavSimplebar';
27876         
27877         for(var i=0;i< children.length;i++) {
27878             
27879             this.buttons.add(this.addxtypeChild(children[i]));
27880             
27881         }
27882         
27883         editor.on('editorevent', this.updateToolbar, this);
27884     },
27885     onBtnClick : function(id)
27886     {
27887        this.editorcore.relayCmd(id);
27888        this.editorcore.focus();
27889     },
27890     
27891     /**
27892      * Protected method that will not generally be called directly. It triggers
27893      * a toolbar update by reading the markup state of the current selection in the editor.
27894      */
27895     updateToolbar: function(){
27896
27897         if(!this.editorcore.activated){
27898             this.editor.onFirstFocus(); // is this neeed?
27899             return;
27900         }
27901
27902         var btns = this.buttons; 
27903         var doc = this.editorcore.doc;
27904         btns.get('bold').setActive(doc.queryCommandState('bold'));
27905         btns.get('italic').setActive(doc.queryCommandState('italic'));
27906         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27907         
27908         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27909         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27910         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27911         
27912         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27913         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27914          /*
27915         
27916         var ans = this.editorcore.getAllAncestors();
27917         if (this.formatCombo) {
27918             
27919             
27920             var store = this.formatCombo.store;
27921             this.formatCombo.setValue("");
27922             for (var i =0; i < ans.length;i++) {
27923                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27924                     // select it..
27925                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27926                     break;
27927                 }
27928             }
27929         }
27930         
27931         
27932         
27933         // hides menus... - so this cant be on a menu...
27934         Roo.bootstrap.MenuMgr.hideAll();
27935         */
27936         Roo.bootstrap.MenuMgr.hideAll();
27937         //this.editorsyncValue();
27938     },
27939     onFirstFocus: function() {
27940         this.buttons.each(function(item){
27941            item.enable();
27942         });
27943     },
27944     toggleSourceEdit : function(sourceEditMode){
27945         
27946           
27947         if(sourceEditMode){
27948             Roo.log("disabling buttons");
27949            this.buttons.each( function(item){
27950                 if(item.cmd != 'pencil'){
27951                     item.disable();
27952                 }
27953             });
27954           
27955         }else{
27956             Roo.log("enabling buttons");
27957             if(this.editorcore.initialized){
27958                 this.buttons.each( function(item){
27959                     item.enable();
27960                 });
27961             }
27962             
27963         }
27964         Roo.log("calling toggole on editor");
27965         // tell the editor that it's been pressed..
27966         this.editor.toggleSourceEdit(sourceEditMode);
27967        
27968     }
27969 });
27970
27971
27972
27973
27974  
27975 /*
27976  * - LGPL
27977  */
27978
27979 /**
27980  * @class Roo.bootstrap.Markdown
27981  * @extends Roo.bootstrap.TextArea
27982  * Bootstrap Showdown editable area
27983  * @cfg {string} content
27984  * 
27985  * @constructor
27986  * Create a new Showdown
27987  */
27988
27989 Roo.bootstrap.Markdown = function(config){
27990     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27991    
27992 };
27993
27994 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27995     
27996     editing :false,
27997     
27998     initEvents : function()
27999     {
28000         
28001         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28002         this.markdownEl = this.el.createChild({
28003             cls : 'roo-markdown-area'
28004         });
28005         this.inputEl().addClass('d-none');
28006         if (this.getValue() == '') {
28007             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28008             
28009         } else {
28010             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28011         }
28012         this.markdownEl.on('click', this.toggleTextEdit, this);
28013         this.on('blur', this.toggleTextEdit, this);
28014         this.on('specialkey', this.resizeTextArea, this);
28015     },
28016     
28017     toggleTextEdit : function()
28018     {
28019         var sh = this.markdownEl.getHeight();
28020         this.inputEl().addClass('d-none');
28021         this.markdownEl.addClass('d-none');
28022         if (!this.editing) {
28023             // show editor?
28024             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28025             this.inputEl().removeClass('d-none');
28026             this.inputEl().focus();
28027             this.editing = true;
28028             return;
28029         }
28030         // show showdown...
28031         this.updateMarkdown();
28032         this.markdownEl.removeClass('d-none');
28033         this.editing = false;
28034         return;
28035     },
28036     updateMarkdown : function()
28037     {
28038         if (this.getValue() == '') {
28039             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28040             return;
28041         }
28042  
28043         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28044     },
28045     
28046     resizeTextArea: function () {
28047         
28048         var sh = 100;
28049         Roo.log([sh, this.getValue().split("\n").length * 30]);
28050         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28051     },
28052     setValue : function(val)
28053     {
28054         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28055         if (!this.editing) {
28056             this.updateMarkdown();
28057         }
28058         
28059     },
28060     focus : function()
28061     {
28062         if (!this.editing) {
28063             this.toggleTextEdit();
28064         }
28065         
28066     }
28067
28068
28069 });/*
28070  * Based on:
28071  * Ext JS Library 1.1.1
28072  * Copyright(c) 2006-2007, Ext JS, LLC.
28073  *
28074  * Originally Released Under LGPL - original licence link has changed is not relivant.
28075  *
28076  * Fork - LGPL
28077  * <script type="text/javascript">
28078  */
28079  
28080 /**
28081  * @class Roo.bootstrap.PagingToolbar
28082  * @extends Roo.bootstrap.NavSimplebar
28083  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28084  * @constructor
28085  * Create a new PagingToolbar
28086  * @param {Object} config The config object
28087  * @param {Roo.data.Store} store
28088  */
28089 Roo.bootstrap.PagingToolbar = function(config)
28090 {
28091     // old args format still supported... - xtype is prefered..
28092         // created from xtype...
28093     
28094     this.ds = config.dataSource;
28095     
28096     if (config.store && !this.ds) {
28097         this.store= Roo.factory(config.store, Roo.data);
28098         this.ds = this.store;
28099         this.ds.xmodule = this.xmodule || false;
28100     }
28101     
28102     this.toolbarItems = [];
28103     if (config.items) {
28104         this.toolbarItems = config.items;
28105     }
28106     
28107     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28108     
28109     this.cursor = 0;
28110     
28111     if (this.ds) { 
28112         this.bind(this.ds);
28113     }
28114     
28115     if (Roo.bootstrap.version == 4) {
28116         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28117     } else {
28118         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28119     }
28120     
28121 };
28122
28123 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28124     /**
28125      * @cfg {Roo.data.Store} dataSource
28126      * The underlying data store providing the paged data
28127      */
28128     /**
28129      * @cfg {String/HTMLElement/Element} container
28130      * container The id or element that will contain the toolbar
28131      */
28132     /**
28133      * @cfg {Boolean} displayInfo
28134      * True to display the displayMsg (defaults to false)
28135      */
28136     /**
28137      * @cfg {Number} pageSize
28138      * The number of records to display per page (defaults to 20)
28139      */
28140     pageSize: 20,
28141     /**
28142      * @cfg {String} displayMsg
28143      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28144      */
28145     displayMsg : 'Displaying {0} - {1} of {2}',
28146     /**
28147      * @cfg {String} emptyMsg
28148      * The message to display when no records are found (defaults to "No data to display")
28149      */
28150     emptyMsg : 'No data to display',
28151     /**
28152      * Customizable piece of the default paging text (defaults to "Page")
28153      * @type String
28154      */
28155     beforePageText : "Page",
28156     /**
28157      * Customizable piece of the default paging text (defaults to "of %0")
28158      * @type String
28159      */
28160     afterPageText : "of {0}",
28161     /**
28162      * Customizable piece of the default paging text (defaults to "First Page")
28163      * @type String
28164      */
28165     firstText : "First Page",
28166     /**
28167      * Customizable piece of the default paging text (defaults to "Previous Page")
28168      * @type String
28169      */
28170     prevText : "Previous Page",
28171     /**
28172      * Customizable piece of the default paging text (defaults to "Next Page")
28173      * @type String
28174      */
28175     nextText : "Next Page",
28176     /**
28177      * Customizable piece of the default paging text (defaults to "Last Page")
28178      * @type String
28179      */
28180     lastText : "Last Page",
28181     /**
28182      * Customizable piece of the default paging text (defaults to "Refresh")
28183      * @type String
28184      */
28185     refreshText : "Refresh",
28186
28187     buttons : false,
28188     // private
28189     onRender : function(ct, position) 
28190     {
28191         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28192         this.navgroup.parentId = this.id;
28193         this.navgroup.onRender(this.el, null);
28194         // add the buttons to the navgroup
28195         
28196         if(this.displayInfo){
28197             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28198             this.displayEl = this.el.select('.x-paging-info', true).first();
28199 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28200 //            this.displayEl = navel.el.select('span',true).first();
28201         }
28202         
28203         var _this = this;
28204         
28205         if(this.buttons){
28206             Roo.each(_this.buttons, function(e){ // this might need to use render????
28207                Roo.factory(e).render(_this.el);
28208             });
28209         }
28210             
28211         Roo.each(_this.toolbarItems, function(e) {
28212             _this.navgroup.addItem(e);
28213         });
28214         
28215         
28216         this.first = this.navgroup.addItem({
28217             tooltip: this.firstText,
28218             cls: "prev btn-outline-secondary",
28219             html : ' <i class="fa fa-step-backward"></i>',
28220             disabled: true,
28221             preventDefault: true,
28222             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28223         });
28224         
28225         this.prev =  this.navgroup.addItem({
28226             tooltip: this.prevText,
28227             cls: "prev btn-outline-secondary",
28228             html : ' <i class="fa fa-backward"></i>',
28229             disabled: true,
28230             preventDefault: true,
28231             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28232         });
28233     //this.addSeparator();
28234         
28235         
28236         var field = this.navgroup.addItem( {
28237             tagtype : 'span',
28238             cls : 'x-paging-position  btn-outline-secondary',
28239              disabled: true,
28240             html : this.beforePageText  +
28241                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28242                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28243          } ); //?? escaped?
28244         
28245         this.field = field.el.select('input', true).first();
28246         this.field.on("keydown", this.onPagingKeydown, this);
28247         this.field.on("focus", function(){this.dom.select();});
28248     
28249     
28250         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28251         //this.field.setHeight(18);
28252         //this.addSeparator();
28253         this.next = this.navgroup.addItem({
28254             tooltip: this.nextText,
28255             cls: "next btn-outline-secondary",
28256             html : ' <i class="fa fa-forward"></i>',
28257             disabled: true,
28258             preventDefault: true,
28259             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28260         });
28261         this.last = this.navgroup.addItem({
28262             tooltip: this.lastText,
28263             html : ' <i class="fa fa-step-forward"></i>',
28264             cls: "next btn-outline-secondary",
28265             disabled: true,
28266             preventDefault: true,
28267             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28268         });
28269     //this.addSeparator();
28270         this.loading = this.navgroup.addItem({
28271             tooltip: this.refreshText,
28272             cls: "btn-outline-secondary",
28273             html : ' <i class="fa fa-refresh"></i>',
28274             preventDefault: true,
28275             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28276         });
28277         
28278     },
28279
28280     // private
28281     updateInfo : function(){
28282         if(this.displayEl){
28283             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28284             var msg = count == 0 ?
28285                 this.emptyMsg :
28286                 String.format(
28287                     this.displayMsg,
28288                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28289                 );
28290             this.displayEl.update(msg);
28291         }
28292     },
28293
28294     // private
28295     onLoad : function(ds, r, o)
28296     {
28297         this.cursor = o.params && o.params.start ? o.params.start : 0;
28298         
28299         var d = this.getPageData(),
28300             ap = d.activePage,
28301             ps = d.pages;
28302         
28303         
28304         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28305         this.field.dom.value = ap;
28306         this.first.setDisabled(ap == 1);
28307         this.prev.setDisabled(ap == 1);
28308         this.next.setDisabled(ap == ps);
28309         this.last.setDisabled(ap == ps);
28310         this.loading.enable();
28311         this.updateInfo();
28312     },
28313
28314     // private
28315     getPageData : function(){
28316         var total = this.ds.getTotalCount();
28317         return {
28318             total : total,
28319             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28320             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28321         };
28322     },
28323
28324     // private
28325     onLoadError : function(){
28326         this.loading.enable();
28327     },
28328
28329     // private
28330     onPagingKeydown : function(e){
28331         var k = e.getKey();
28332         var d = this.getPageData();
28333         if(k == e.RETURN){
28334             var v = this.field.dom.value, pageNum;
28335             if(!v || isNaN(pageNum = parseInt(v, 10))){
28336                 this.field.dom.value = d.activePage;
28337                 return;
28338             }
28339             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28340             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28341             e.stopEvent();
28342         }
28343         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))
28344         {
28345           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28346           this.field.dom.value = pageNum;
28347           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28348           e.stopEvent();
28349         }
28350         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28351         {
28352           var v = this.field.dom.value, pageNum; 
28353           var increment = (e.shiftKey) ? 10 : 1;
28354           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28355                 increment *= -1;
28356           }
28357           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28358             this.field.dom.value = d.activePage;
28359             return;
28360           }
28361           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28362           {
28363             this.field.dom.value = parseInt(v, 10) + increment;
28364             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28365             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28366           }
28367           e.stopEvent();
28368         }
28369     },
28370
28371     // private
28372     beforeLoad : function(){
28373         if(this.loading){
28374             this.loading.disable();
28375         }
28376     },
28377
28378     // private
28379     onClick : function(which){
28380         
28381         var ds = this.ds;
28382         if (!ds) {
28383             return;
28384         }
28385         
28386         switch(which){
28387             case "first":
28388                 ds.load({params:{start: 0, limit: this.pageSize}});
28389             break;
28390             case "prev":
28391                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28392             break;
28393             case "next":
28394                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28395             break;
28396             case "last":
28397                 var total = ds.getTotalCount();
28398                 var extra = total % this.pageSize;
28399                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28400                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28401             break;
28402             case "refresh":
28403                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28404             break;
28405         }
28406     },
28407
28408     /**
28409      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28410      * @param {Roo.data.Store} store The data store to unbind
28411      */
28412     unbind : function(ds){
28413         ds.un("beforeload", this.beforeLoad, this);
28414         ds.un("load", this.onLoad, this);
28415         ds.un("loadexception", this.onLoadError, this);
28416         ds.un("remove", this.updateInfo, this);
28417         ds.un("add", this.updateInfo, this);
28418         this.ds = undefined;
28419     },
28420
28421     /**
28422      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28423      * @param {Roo.data.Store} store The data store to bind
28424      */
28425     bind : function(ds){
28426         ds.on("beforeload", this.beforeLoad, this);
28427         ds.on("load", this.onLoad, this);
28428         ds.on("loadexception", this.onLoadError, this);
28429         ds.on("remove", this.updateInfo, this);
28430         ds.on("add", this.updateInfo, this);
28431         this.ds = ds;
28432     }
28433 });/*
28434  * - LGPL
28435  *
28436  * element
28437  * 
28438  */
28439
28440 /**
28441  * @class Roo.bootstrap.MessageBar
28442  * @extends Roo.bootstrap.Component
28443  * Bootstrap MessageBar class
28444  * @cfg {String} html contents of the MessageBar
28445  * @cfg {String} weight (info | success | warning | danger) default info
28446  * @cfg {String} beforeClass insert the bar before the given class
28447  * @cfg {Boolean} closable (true | false) default false
28448  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28449  * 
28450  * @constructor
28451  * Create a new Element
28452  * @param {Object} config The config object
28453  */
28454
28455 Roo.bootstrap.MessageBar = function(config){
28456     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28457 };
28458
28459 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28460     
28461     html: '',
28462     weight: 'info',
28463     closable: false,
28464     fixed: false,
28465     beforeClass: 'bootstrap-sticky-wrap',
28466     
28467     getAutoCreate : function(){
28468         
28469         var cfg = {
28470             tag: 'div',
28471             cls: 'alert alert-dismissable alert-' + this.weight,
28472             cn: [
28473                 {
28474                     tag: 'span',
28475                     cls: 'message',
28476                     html: this.html || ''
28477                 }
28478             ]
28479         };
28480         
28481         if(this.fixed){
28482             cfg.cls += ' alert-messages-fixed';
28483         }
28484         
28485         if(this.closable){
28486             cfg.cn.push({
28487                 tag: 'button',
28488                 cls: 'close',
28489                 html: 'x'
28490             });
28491         }
28492         
28493         return cfg;
28494     },
28495     
28496     onRender : function(ct, position)
28497     {
28498         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28499         
28500         if(!this.el){
28501             var cfg = Roo.apply({},  this.getAutoCreate());
28502             cfg.id = Roo.id();
28503             
28504             if (this.cls) {
28505                 cfg.cls += ' ' + this.cls;
28506             }
28507             if (this.style) {
28508                 cfg.style = this.style;
28509             }
28510             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28511             
28512             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28513         }
28514         
28515         this.el.select('>button.close').on('click', this.hide, this);
28516         
28517     },
28518     
28519     show : function()
28520     {
28521         if (!this.rendered) {
28522             this.render();
28523         }
28524         
28525         this.el.show();
28526         
28527         this.fireEvent('show', this);
28528         
28529     },
28530     
28531     hide : function()
28532     {
28533         if (!this.rendered) {
28534             this.render();
28535         }
28536         
28537         this.el.hide();
28538         
28539         this.fireEvent('hide', this);
28540     },
28541     
28542     update : function()
28543     {
28544 //        var e = this.el.dom.firstChild;
28545 //        
28546 //        if(this.closable){
28547 //            e = e.nextSibling;
28548 //        }
28549 //        
28550 //        e.data = this.html || '';
28551
28552         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28553     }
28554    
28555 });
28556
28557  
28558
28559      /*
28560  * - LGPL
28561  *
28562  * Graph
28563  * 
28564  */
28565
28566
28567 /**
28568  * @class Roo.bootstrap.Graph
28569  * @extends Roo.bootstrap.Component
28570  * Bootstrap Graph class
28571 > Prameters
28572  -sm {number} sm 4
28573  -md {number} md 5
28574  @cfg {String} graphtype  bar | vbar | pie
28575  @cfg {number} g_x coodinator | centre x (pie)
28576  @cfg {number} g_y coodinator | centre y (pie)
28577  @cfg {number} g_r radius (pie)
28578  @cfg {number} g_height height of the chart (respected by all elements in the set)
28579  @cfg {number} g_width width of the chart (respected by all elements in the set)
28580  @cfg {Object} title The title of the chart
28581     
28582  -{Array}  values
28583  -opts (object) options for the chart 
28584      o {
28585      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28586      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28587      o vgutter (number)
28588      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.
28589      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28590      o to
28591      o stretch (boolean)
28592      o }
28593  -opts (object) options for the pie
28594      o{
28595      o cut
28596      o startAngle (number)
28597      o endAngle (number)
28598      } 
28599  *
28600  * @constructor
28601  * Create a new Input
28602  * @param {Object} config The config object
28603  */
28604
28605 Roo.bootstrap.Graph = function(config){
28606     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28607     
28608     this.addEvents({
28609         // img events
28610         /**
28611          * @event click
28612          * The img click event for the img.
28613          * @param {Roo.EventObject} e
28614          */
28615         "click" : true
28616     });
28617 };
28618
28619 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28620     
28621     sm: 4,
28622     md: 5,
28623     graphtype: 'bar',
28624     g_height: 250,
28625     g_width: 400,
28626     g_x: 50,
28627     g_y: 50,
28628     g_r: 30,
28629     opts:{
28630         //g_colors: this.colors,
28631         g_type: 'soft',
28632         g_gutter: '20%'
28633
28634     },
28635     title : false,
28636
28637     getAutoCreate : function(){
28638         
28639         var cfg = {
28640             tag: 'div',
28641             html : null
28642         };
28643         
28644         
28645         return  cfg;
28646     },
28647
28648     onRender : function(ct,position){
28649         
28650         
28651         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28652         
28653         if (typeof(Raphael) == 'undefined') {
28654             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28655             return;
28656         }
28657         
28658         this.raphael = Raphael(this.el.dom);
28659         
28660                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28661                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28662                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28663                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28664                 /*
28665                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28666                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28667                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28668                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28669                 
28670                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28671                 r.barchart(330, 10, 300, 220, data1);
28672                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28673                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28674                 */
28675                 
28676                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28677                 // r.barchart(30, 30, 560, 250,  xdata, {
28678                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28679                 //     axis : "0 0 1 1",
28680                 //     axisxlabels :  xdata
28681                 //     //yvalues : cols,
28682                    
28683                 // });
28684 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28685 //        
28686 //        this.load(null,xdata,{
28687 //                axis : "0 0 1 1",
28688 //                axisxlabels :  xdata
28689 //                });
28690
28691     },
28692
28693     load : function(graphtype,xdata,opts)
28694     {
28695         this.raphael.clear();
28696         if(!graphtype) {
28697             graphtype = this.graphtype;
28698         }
28699         if(!opts){
28700             opts = this.opts;
28701         }
28702         var r = this.raphael,
28703             fin = function () {
28704                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28705             },
28706             fout = function () {
28707                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28708             },
28709             pfin = function() {
28710                 this.sector.stop();
28711                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28712
28713                 if (this.label) {
28714                     this.label[0].stop();
28715                     this.label[0].attr({ r: 7.5 });
28716                     this.label[1].attr({ "font-weight": 800 });
28717                 }
28718             },
28719             pfout = function() {
28720                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28721
28722                 if (this.label) {
28723                     this.label[0].animate({ r: 5 }, 500, "bounce");
28724                     this.label[1].attr({ "font-weight": 400 });
28725                 }
28726             };
28727
28728         switch(graphtype){
28729             case 'bar':
28730                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28731                 break;
28732             case 'hbar':
28733                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28734                 break;
28735             case 'pie':
28736 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28737 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28738 //            
28739                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28740                 
28741                 break;
28742
28743         }
28744         
28745         if(this.title){
28746             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28747         }
28748         
28749     },
28750     
28751     setTitle: function(o)
28752     {
28753         this.title = o;
28754     },
28755     
28756     initEvents: function() {
28757         
28758         if(!this.href){
28759             this.el.on('click', this.onClick, this);
28760         }
28761     },
28762     
28763     onClick : function(e)
28764     {
28765         Roo.log('img onclick');
28766         this.fireEvent('click', this, e);
28767     }
28768    
28769 });
28770
28771  
28772 /*
28773  * - LGPL
28774  *
28775  * numberBox
28776  * 
28777  */
28778 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28779
28780 /**
28781  * @class Roo.bootstrap.dash.NumberBox
28782  * @extends Roo.bootstrap.Component
28783  * Bootstrap NumberBox class
28784  * @cfg {String} headline Box headline
28785  * @cfg {String} content Box content
28786  * @cfg {String} icon Box icon
28787  * @cfg {String} footer Footer text
28788  * @cfg {String} fhref Footer href
28789  * 
28790  * @constructor
28791  * Create a new NumberBox
28792  * @param {Object} config The config object
28793  */
28794
28795
28796 Roo.bootstrap.dash.NumberBox = function(config){
28797     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28798     
28799 };
28800
28801 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28802     
28803     headline : '',
28804     content : '',
28805     icon : '',
28806     footer : '',
28807     fhref : '',
28808     ficon : '',
28809     
28810     getAutoCreate : function(){
28811         
28812         var cfg = {
28813             tag : 'div',
28814             cls : 'small-box ',
28815             cn : [
28816                 {
28817                     tag : 'div',
28818                     cls : 'inner',
28819                     cn :[
28820                         {
28821                             tag : 'h3',
28822                             cls : 'roo-headline',
28823                             html : this.headline
28824                         },
28825                         {
28826                             tag : 'p',
28827                             cls : 'roo-content',
28828                             html : this.content
28829                         }
28830                     ]
28831                 }
28832             ]
28833         };
28834         
28835         if(this.icon){
28836             cfg.cn.push({
28837                 tag : 'div',
28838                 cls : 'icon',
28839                 cn :[
28840                     {
28841                         tag : 'i',
28842                         cls : 'ion ' + this.icon
28843                     }
28844                 ]
28845             });
28846         }
28847         
28848         if(this.footer){
28849             var footer = {
28850                 tag : 'a',
28851                 cls : 'small-box-footer',
28852                 href : this.fhref || '#',
28853                 html : this.footer
28854             };
28855             
28856             cfg.cn.push(footer);
28857             
28858         }
28859         
28860         return  cfg;
28861     },
28862
28863     onRender : function(ct,position){
28864         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28865
28866
28867        
28868                 
28869     },
28870
28871     setHeadline: function (value)
28872     {
28873         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28874     },
28875     
28876     setFooter: function (value, href)
28877     {
28878         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28879         
28880         if(href){
28881             this.el.select('a.small-box-footer',true).first().attr('href', href);
28882         }
28883         
28884     },
28885
28886     setContent: function (value)
28887     {
28888         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28889     },
28890
28891     initEvents: function() 
28892     {   
28893         
28894     }
28895     
28896 });
28897
28898  
28899 /*
28900  * - LGPL
28901  *
28902  * TabBox
28903  * 
28904  */
28905 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28906
28907 /**
28908  * @class Roo.bootstrap.dash.TabBox
28909  * @extends Roo.bootstrap.Component
28910  * Bootstrap TabBox class
28911  * @cfg {String} title Title of the TabBox
28912  * @cfg {String} icon Icon of the TabBox
28913  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28914  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28915  * 
28916  * @constructor
28917  * Create a new TabBox
28918  * @param {Object} config The config object
28919  */
28920
28921
28922 Roo.bootstrap.dash.TabBox = function(config){
28923     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28924     this.addEvents({
28925         // raw events
28926         /**
28927          * @event addpane
28928          * When a pane is added
28929          * @param {Roo.bootstrap.dash.TabPane} pane
28930          */
28931         "addpane" : true,
28932         /**
28933          * @event activatepane
28934          * When a pane is activated
28935          * @param {Roo.bootstrap.dash.TabPane} pane
28936          */
28937         "activatepane" : true
28938         
28939          
28940     });
28941     
28942     this.panes = [];
28943 };
28944
28945 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28946
28947     title : '',
28948     icon : false,
28949     showtabs : true,
28950     tabScrollable : false,
28951     
28952     getChildContainer : function()
28953     {
28954         return this.el.select('.tab-content', true).first();
28955     },
28956     
28957     getAutoCreate : function(){
28958         
28959         var header = {
28960             tag: 'li',
28961             cls: 'pull-left header',
28962             html: this.title,
28963             cn : []
28964         };
28965         
28966         if(this.icon){
28967             header.cn.push({
28968                 tag: 'i',
28969                 cls: 'fa ' + this.icon
28970             });
28971         }
28972         
28973         var h = {
28974             tag: 'ul',
28975             cls: 'nav nav-tabs pull-right',
28976             cn: [
28977                 header
28978             ]
28979         };
28980         
28981         if(this.tabScrollable){
28982             h = {
28983                 tag: 'div',
28984                 cls: 'tab-header',
28985                 cn: [
28986                     {
28987                         tag: 'ul',
28988                         cls: 'nav nav-tabs pull-right',
28989                         cn: [
28990                             header
28991                         ]
28992                     }
28993                 ]
28994             };
28995         }
28996         
28997         var cfg = {
28998             tag: 'div',
28999             cls: 'nav-tabs-custom',
29000             cn: [
29001                 h,
29002                 {
29003                     tag: 'div',
29004                     cls: 'tab-content no-padding',
29005                     cn: []
29006                 }
29007             ]
29008         };
29009
29010         return  cfg;
29011     },
29012     initEvents : function()
29013     {
29014         //Roo.log('add add pane handler');
29015         this.on('addpane', this.onAddPane, this);
29016     },
29017      /**
29018      * Updates the box title
29019      * @param {String} html to set the title to.
29020      */
29021     setTitle : function(value)
29022     {
29023         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29024     },
29025     onAddPane : function(pane)
29026     {
29027         this.panes.push(pane);
29028         //Roo.log('addpane');
29029         //Roo.log(pane);
29030         // tabs are rendere left to right..
29031         if(!this.showtabs){
29032             return;
29033         }
29034         
29035         var ctr = this.el.select('.nav-tabs', true).first();
29036          
29037          
29038         var existing = ctr.select('.nav-tab',true);
29039         var qty = existing.getCount();;
29040         
29041         
29042         var tab = ctr.createChild({
29043             tag : 'li',
29044             cls : 'nav-tab' + (qty ? '' : ' active'),
29045             cn : [
29046                 {
29047                     tag : 'a',
29048                     href:'#',
29049                     html : pane.title
29050                 }
29051             ]
29052         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29053         pane.tab = tab;
29054         
29055         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29056         if (!qty) {
29057             pane.el.addClass('active');
29058         }
29059         
29060                 
29061     },
29062     onTabClick : function(ev,un,ob,pane)
29063     {
29064         //Roo.log('tab - prev default');
29065         ev.preventDefault();
29066         
29067         
29068         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29069         pane.tab.addClass('active');
29070         //Roo.log(pane.title);
29071         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29072         // technically we should have a deactivate event.. but maybe add later.
29073         // and it should not de-activate the selected tab...
29074         this.fireEvent('activatepane', pane);
29075         pane.el.addClass('active');
29076         pane.fireEvent('activate');
29077         
29078         
29079     },
29080     
29081     getActivePane : function()
29082     {
29083         var r = false;
29084         Roo.each(this.panes, function(p) {
29085             if(p.el.hasClass('active')){
29086                 r = p;
29087                 return false;
29088             }
29089             
29090             return;
29091         });
29092         
29093         return r;
29094     }
29095     
29096     
29097 });
29098
29099  
29100 /*
29101  * - LGPL
29102  *
29103  * Tab pane
29104  * 
29105  */
29106 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29107 /**
29108  * @class Roo.bootstrap.TabPane
29109  * @extends Roo.bootstrap.Component
29110  * Bootstrap TabPane class
29111  * @cfg {Boolean} active (false | true) Default false
29112  * @cfg {String} title title of panel
29113
29114  * 
29115  * @constructor
29116  * Create a new TabPane
29117  * @param {Object} config The config object
29118  */
29119
29120 Roo.bootstrap.dash.TabPane = function(config){
29121     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29122     
29123     this.addEvents({
29124         // raw events
29125         /**
29126          * @event activate
29127          * When a pane is activated
29128          * @param {Roo.bootstrap.dash.TabPane} pane
29129          */
29130         "activate" : true
29131          
29132     });
29133 };
29134
29135 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29136     
29137     active : false,
29138     title : '',
29139     
29140     // the tabBox that this is attached to.
29141     tab : false,
29142      
29143     getAutoCreate : function() 
29144     {
29145         var cfg = {
29146             tag: 'div',
29147             cls: 'tab-pane'
29148         };
29149         
29150         if(this.active){
29151             cfg.cls += ' active';
29152         }
29153         
29154         return cfg;
29155     },
29156     initEvents  : function()
29157     {
29158         //Roo.log('trigger add pane handler');
29159         this.parent().fireEvent('addpane', this)
29160     },
29161     
29162      /**
29163      * Updates the tab title 
29164      * @param {String} html to set the title to.
29165      */
29166     setTitle: function(str)
29167     {
29168         if (!this.tab) {
29169             return;
29170         }
29171         this.title = str;
29172         this.tab.select('a', true).first().dom.innerHTML = str;
29173         
29174     }
29175     
29176     
29177     
29178 });
29179
29180  
29181
29182
29183  /*
29184  * - LGPL
29185  *
29186  * menu
29187  * 
29188  */
29189 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29190
29191 /**
29192  * @class Roo.bootstrap.menu.Menu
29193  * @extends Roo.bootstrap.Component
29194  * Bootstrap Menu class - container for Menu
29195  * @cfg {String} html Text of the menu
29196  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29197  * @cfg {String} icon Font awesome icon
29198  * @cfg {String} pos Menu align to (top | bottom) default bottom
29199  * 
29200  * 
29201  * @constructor
29202  * Create a new Menu
29203  * @param {Object} config The config object
29204  */
29205
29206
29207 Roo.bootstrap.menu.Menu = function(config){
29208     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29209     
29210     this.addEvents({
29211         /**
29212          * @event beforeshow
29213          * Fires before this menu is displayed
29214          * @param {Roo.bootstrap.menu.Menu} this
29215          */
29216         beforeshow : true,
29217         /**
29218          * @event beforehide
29219          * Fires before this menu is hidden
29220          * @param {Roo.bootstrap.menu.Menu} this
29221          */
29222         beforehide : true,
29223         /**
29224          * @event show
29225          * Fires after this menu is displayed
29226          * @param {Roo.bootstrap.menu.Menu} this
29227          */
29228         show : true,
29229         /**
29230          * @event hide
29231          * Fires after this menu is hidden
29232          * @param {Roo.bootstrap.menu.Menu} this
29233          */
29234         hide : true,
29235         /**
29236          * @event click
29237          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29238          * @param {Roo.bootstrap.menu.Menu} this
29239          * @param {Roo.EventObject} e
29240          */
29241         click : true
29242     });
29243     
29244 };
29245
29246 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29247     
29248     submenu : false,
29249     html : '',
29250     weight : 'default',
29251     icon : false,
29252     pos : 'bottom',
29253     
29254     
29255     getChildContainer : function() {
29256         if(this.isSubMenu){
29257             return this.el;
29258         }
29259         
29260         return this.el.select('ul.dropdown-menu', true).first();  
29261     },
29262     
29263     getAutoCreate : function()
29264     {
29265         var text = [
29266             {
29267                 tag : 'span',
29268                 cls : 'roo-menu-text',
29269                 html : this.html
29270             }
29271         ];
29272         
29273         if(this.icon){
29274             text.unshift({
29275                 tag : 'i',
29276                 cls : 'fa ' + this.icon
29277             })
29278         }
29279         
29280         
29281         var cfg = {
29282             tag : 'div',
29283             cls : 'btn-group',
29284             cn : [
29285                 {
29286                     tag : 'button',
29287                     cls : 'dropdown-button btn btn-' + this.weight,
29288                     cn : text
29289                 },
29290                 {
29291                     tag : 'button',
29292                     cls : 'dropdown-toggle btn btn-' + this.weight,
29293                     cn : [
29294                         {
29295                             tag : 'span',
29296                             cls : 'caret'
29297                         }
29298                     ]
29299                 },
29300                 {
29301                     tag : 'ul',
29302                     cls : 'dropdown-menu'
29303                 }
29304             ]
29305             
29306         };
29307         
29308         if(this.pos == 'top'){
29309             cfg.cls += ' dropup';
29310         }
29311         
29312         if(this.isSubMenu){
29313             cfg = {
29314                 tag : 'ul',
29315                 cls : 'dropdown-menu'
29316             }
29317         }
29318         
29319         return cfg;
29320     },
29321     
29322     onRender : function(ct, position)
29323     {
29324         this.isSubMenu = ct.hasClass('dropdown-submenu');
29325         
29326         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29327     },
29328     
29329     initEvents : function() 
29330     {
29331         if(this.isSubMenu){
29332             return;
29333         }
29334         
29335         this.hidden = true;
29336         
29337         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29338         this.triggerEl.on('click', this.onTriggerPress, this);
29339         
29340         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29341         this.buttonEl.on('click', this.onClick, this);
29342         
29343     },
29344     
29345     list : function()
29346     {
29347         if(this.isSubMenu){
29348             return this.el;
29349         }
29350         
29351         return this.el.select('ul.dropdown-menu', true).first();
29352     },
29353     
29354     onClick : function(e)
29355     {
29356         this.fireEvent("click", this, e);
29357     },
29358     
29359     onTriggerPress  : function(e)
29360     {   
29361         if (this.isVisible()) {
29362             this.hide();
29363         } else {
29364             this.show();
29365         }
29366     },
29367     
29368     isVisible : function(){
29369         return !this.hidden;
29370     },
29371     
29372     show : function()
29373     {
29374         this.fireEvent("beforeshow", this);
29375         
29376         this.hidden = false;
29377         this.el.addClass('open');
29378         
29379         Roo.get(document).on("mouseup", this.onMouseUp, this);
29380         
29381         this.fireEvent("show", this);
29382         
29383         
29384     },
29385     
29386     hide : function()
29387     {
29388         this.fireEvent("beforehide", this);
29389         
29390         this.hidden = true;
29391         this.el.removeClass('open');
29392         
29393         Roo.get(document).un("mouseup", this.onMouseUp);
29394         
29395         this.fireEvent("hide", this);
29396     },
29397     
29398     onMouseUp : function()
29399     {
29400         this.hide();
29401     }
29402     
29403 });
29404
29405  
29406  /*
29407  * - LGPL
29408  *
29409  * menu item
29410  * 
29411  */
29412 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29413
29414 /**
29415  * @class Roo.bootstrap.menu.Item
29416  * @extends Roo.bootstrap.Component
29417  * Bootstrap MenuItem class
29418  * @cfg {Boolean} submenu (true | false) default false
29419  * @cfg {String} html text of the item
29420  * @cfg {String} href the link
29421  * @cfg {Boolean} disable (true | false) default false
29422  * @cfg {Boolean} preventDefault (true | false) default true
29423  * @cfg {String} icon Font awesome icon
29424  * @cfg {String} pos Submenu align to (left | right) default right 
29425  * 
29426  * 
29427  * @constructor
29428  * Create a new Item
29429  * @param {Object} config The config object
29430  */
29431
29432
29433 Roo.bootstrap.menu.Item = function(config){
29434     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29435     this.addEvents({
29436         /**
29437          * @event mouseover
29438          * Fires when the mouse is hovering over this menu
29439          * @param {Roo.bootstrap.menu.Item} this
29440          * @param {Roo.EventObject} e
29441          */
29442         mouseover : true,
29443         /**
29444          * @event mouseout
29445          * Fires when the mouse exits this menu
29446          * @param {Roo.bootstrap.menu.Item} this
29447          * @param {Roo.EventObject} e
29448          */
29449         mouseout : true,
29450         // raw events
29451         /**
29452          * @event click
29453          * The raw click event for the entire grid.
29454          * @param {Roo.EventObject} e
29455          */
29456         click : true
29457     });
29458 };
29459
29460 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29461     
29462     submenu : false,
29463     href : '',
29464     html : '',
29465     preventDefault: true,
29466     disable : false,
29467     icon : false,
29468     pos : 'right',
29469     
29470     getAutoCreate : function()
29471     {
29472         var text = [
29473             {
29474                 tag : 'span',
29475                 cls : 'roo-menu-item-text',
29476                 html : this.html
29477             }
29478         ];
29479         
29480         if(this.icon){
29481             text.unshift({
29482                 tag : 'i',
29483                 cls : 'fa ' + this.icon
29484             })
29485         }
29486         
29487         var cfg = {
29488             tag : 'li',
29489             cn : [
29490                 {
29491                     tag : 'a',
29492                     href : this.href || '#',
29493                     cn : text
29494                 }
29495             ]
29496         };
29497         
29498         if(this.disable){
29499             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29500         }
29501         
29502         if(this.submenu){
29503             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29504             
29505             if(this.pos == 'left'){
29506                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29507             }
29508         }
29509         
29510         return cfg;
29511     },
29512     
29513     initEvents : function() 
29514     {
29515         this.el.on('mouseover', this.onMouseOver, this);
29516         this.el.on('mouseout', this.onMouseOut, this);
29517         
29518         this.el.select('a', true).first().on('click', this.onClick, this);
29519         
29520     },
29521     
29522     onClick : function(e)
29523     {
29524         if(this.preventDefault){
29525             e.preventDefault();
29526         }
29527         
29528         this.fireEvent("click", this, e);
29529     },
29530     
29531     onMouseOver : function(e)
29532     {
29533         if(this.submenu && this.pos == 'left'){
29534             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29535         }
29536         
29537         this.fireEvent("mouseover", this, e);
29538     },
29539     
29540     onMouseOut : function(e)
29541     {
29542         this.fireEvent("mouseout", this, e);
29543     }
29544 });
29545
29546  
29547
29548  /*
29549  * - LGPL
29550  *
29551  * menu separator
29552  * 
29553  */
29554 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29555
29556 /**
29557  * @class Roo.bootstrap.menu.Separator
29558  * @extends Roo.bootstrap.Component
29559  * Bootstrap Separator class
29560  * 
29561  * @constructor
29562  * Create a new Separator
29563  * @param {Object} config The config object
29564  */
29565
29566
29567 Roo.bootstrap.menu.Separator = function(config){
29568     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29569 };
29570
29571 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29572     
29573     getAutoCreate : function(){
29574         var cfg = {
29575             tag : 'li',
29576             cls: 'dropdown-divider divider'
29577         };
29578         
29579         return cfg;
29580     }
29581    
29582 });
29583
29584  
29585
29586  /*
29587  * - LGPL
29588  *
29589  * Tooltip
29590  * 
29591  */
29592
29593 /**
29594  * @class Roo.bootstrap.Tooltip
29595  * Bootstrap Tooltip class
29596  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29597  * to determine which dom element triggers the tooltip.
29598  * 
29599  * It needs to add support for additional attributes like tooltip-position
29600  * 
29601  * @constructor
29602  * Create a new Toolti
29603  * @param {Object} config The config object
29604  */
29605
29606 Roo.bootstrap.Tooltip = function(config){
29607     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29608     
29609     this.alignment = Roo.bootstrap.Tooltip.alignment;
29610     
29611     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29612         this.alignment = config.alignment;
29613     }
29614     
29615 };
29616
29617 Roo.apply(Roo.bootstrap.Tooltip, {
29618     /**
29619      * @function init initialize tooltip monitoring.
29620      * @static
29621      */
29622     currentEl : false,
29623     currentTip : false,
29624     currentRegion : false,
29625     
29626     //  init : delay?
29627     
29628     init : function()
29629     {
29630         Roo.get(document).on('mouseover', this.enter ,this);
29631         Roo.get(document).on('mouseout', this.leave, this);
29632          
29633         
29634         this.currentTip = new Roo.bootstrap.Tooltip();
29635     },
29636     
29637     enter : function(ev)
29638     {
29639         var dom = ev.getTarget();
29640         
29641         //Roo.log(['enter',dom]);
29642         var el = Roo.fly(dom);
29643         if (this.currentEl) {
29644             //Roo.log(dom);
29645             //Roo.log(this.currentEl);
29646             //Roo.log(this.currentEl.contains(dom));
29647             if (this.currentEl == el) {
29648                 return;
29649             }
29650             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29651                 return;
29652             }
29653
29654         }
29655         
29656         if (this.currentTip.el) {
29657             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29658         }    
29659         //Roo.log(ev);
29660         
29661         if(!el || el.dom == document){
29662             return;
29663         }
29664         
29665         var bindEl = el; 
29666         var pel = false;
29667         if (!el.attr('tooltip')) {
29668             pel = el.findParent("[tooltip]");
29669             if (pel) {
29670                 bindEl = Roo.get(pel);
29671             }
29672         }
29673         
29674        
29675         
29676         // you can not look for children, as if el is the body.. then everythign is the child..
29677         if (!pel && !el.attr('tooltip')) { //
29678             if (!el.select("[tooltip]").elements.length) {
29679                 return;
29680             }
29681             // is the mouse over this child...?
29682             bindEl = el.select("[tooltip]").first();
29683             var xy = ev.getXY();
29684             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29685                 //Roo.log("not in region.");
29686                 return;
29687             }
29688             //Roo.log("child element over..");
29689             
29690         }
29691         this.currentEl = el;
29692         this.currentTip.bind(bindEl);
29693         this.currentRegion = Roo.lib.Region.getRegion(dom);
29694         this.currentTip.enter();
29695         
29696     },
29697     leave : function(ev)
29698     {
29699         var dom = ev.getTarget();
29700         //Roo.log(['leave',dom]);
29701         if (!this.currentEl) {
29702             return;
29703         }
29704         
29705         
29706         if (dom != this.currentEl.dom) {
29707             return;
29708         }
29709         var xy = ev.getXY();
29710         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29711             return;
29712         }
29713         // only activate leave if mouse cursor is outside... bounding box..
29714         
29715         
29716         
29717         
29718         if (this.currentTip) {
29719             this.currentTip.leave();
29720         }
29721         //Roo.log('clear currentEl');
29722         this.currentEl = false;
29723         
29724         
29725     },
29726     alignment : {
29727         'left' : ['r-l', [-2,0], 'right'],
29728         'right' : ['l-r', [2,0], 'left'],
29729         'bottom' : ['t-b', [0,2], 'top'],
29730         'top' : [ 'b-t', [0,-2], 'bottom']
29731     }
29732     
29733 });
29734
29735
29736 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29737     
29738     
29739     bindEl : false,
29740     
29741     delay : null, // can be { show : 300 , hide: 500}
29742     
29743     timeout : null,
29744     
29745     hoverState : null, //???
29746     
29747     placement : 'bottom', 
29748     
29749     alignment : false,
29750     
29751     getAutoCreate : function(){
29752     
29753         var cfg = {
29754            cls : 'tooltip',   
29755            role : 'tooltip',
29756            cn : [
29757                 {
29758                     cls : 'tooltip-arrow arrow'
29759                 },
29760                 {
29761                     cls : 'tooltip-inner'
29762                 }
29763            ]
29764         };
29765         
29766         return cfg;
29767     },
29768     bind : function(el)
29769     {
29770         this.bindEl = el;
29771     },
29772     
29773     initEvents : function()
29774     {
29775         this.arrowEl = this.el.select('.arrow', true).first();
29776         this.innerEl = this.el.select('.tooltip-inner', true).first();
29777     },
29778     
29779     enter : function () {
29780        
29781         if (this.timeout != null) {
29782             clearTimeout(this.timeout);
29783         }
29784         
29785         this.hoverState = 'in';
29786          //Roo.log("enter - show");
29787         if (!this.delay || !this.delay.show) {
29788             this.show();
29789             return;
29790         }
29791         var _t = this;
29792         this.timeout = setTimeout(function () {
29793             if (_t.hoverState == 'in') {
29794                 _t.show();
29795             }
29796         }, this.delay.show);
29797     },
29798     leave : function()
29799     {
29800         clearTimeout(this.timeout);
29801     
29802         this.hoverState = 'out';
29803          if (!this.delay || !this.delay.hide) {
29804             this.hide();
29805             return;
29806         }
29807        
29808         var _t = this;
29809         this.timeout = setTimeout(function () {
29810             //Roo.log("leave - timeout");
29811             
29812             if (_t.hoverState == 'out') {
29813                 _t.hide();
29814                 Roo.bootstrap.Tooltip.currentEl = false;
29815             }
29816         }, delay);
29817     },
29818     
29819     show : function (msg)
29820     {
29821         if (!this.el) {
29822             this.render(document.body);
29823         }
29824         // set content.
29825         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29826         
29827         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29828         
29829         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29830         
29831         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29832                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29833         
29834         var placement = typeof this.placement == 'function' ?
29835             this.placement.call(this, this.el, on_el) :
29836             this.placement;
29837             
29838         var autoToken = /\s?auto?\s?/i;
29839         var autoPlace = autoToken.test(placement);
29840         if (autoPlace) {
29841             placement = placement.replace(autoToken, '') || 'top';
29842         }
29843         
29844         //this.el.detach()
29845         //this.el.setXY([0,0]);
29846         this.el.show();
29847         //this.el.dom.style.display='block';
29848         
29849         //this.el.appendTo(on_el);
29850         
29851         var p = this.getPosition();
29852         var box = this.el.getBox();
29853         
29854         if (autoPlace) {
29855             // fixme..
29856         }
29857         
29858         var align = this.alignment[placement];
29859         
29860         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29861         
29862         if(placement == 'top' || placement == 'bottom'){
29863             if(xy[0] < 0){
29864                 placement = 'right';
29865             }
29866             
29867             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29868                 placement = 'left';
29869             }
29870             
29871             var scroll = Roo.select('body', true).first().getScroll();
29872             
29873             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29874                 placement = 'top';
29875             }
29876             
29877             align = this.alignment[placement];
29878             
29879             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29880             
29881         }
29882         
29883         var elems = document.getElementsByTagName('div');
29884         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29885         for (var i = 0; i < elems.length; i++) {
29886           var zindex = Number.parseInt(
29887                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29888                 10
29889           );
29890           if (zindex > highest) {
29891             highest = zindex;
29892           }
29893         }
29894         
29895         
29896         
29897         this.el.dom.style.zIndex = highest;
29898         
29899         this.el.alignTo(this.bindEl, align[0],align[1]);
29900         //var arrow = this.el.select('.arrow',true).first();
29901         //arrow.set(align[2], 
29902         
29903         this.el.addClass(placement);
29904         this.el.addClass("bs-tooltip-"+ placement);
29905         
29906         this.el.addClass('in fade show');
29907         
29908         this.hoverState = null;
29909         
29910         if (this.el.hasClass('fade')) {
29911             // fade it?
29912         }
29913         
29914         
29915         
29916         
29917         
29918     },
29919     hide : function()
29920     {
29921          
29922         if (!this.el) {
29923             return;
29924         }
29925         //this.el.setXY([0,0]);
29926         this.el.removeClass(['show', 'in']);
29927         //this.el.hide();
29928         
29929     }
29930     
29931 });
29932  
29933
29934  /*
29935  * - LGPL
29936  *
29937  * Location Picker
29938  * 
29939  */
29940
29941 /**
29942  * @class Roo.bootstrap.LocationPicker
29943  * @extends Roo.bootstrap.Component
29944  * Bootstrap LocationPicker class
29945  * @cfg {Number} latitude Position when init default 0
29946  * @cfg {Number} longitude Position when init default 0
29947  * @cfg {Number} zoom default 15
29948  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29949  * @cfg {Boolean} mapTypeControl default false
29950  * @cfg {Boolean} disableDoubleClickZoom default false
29951  * @cfg {Boolean} scrollwheel default true
29952  * @cfg {Boolean} streetViewControl default false
29953  * @cfg {Number} radius default 0
29954  * @cfg {String} locationName
29955  * @cfg {Boolean} draggable default true
29956  * @cfg {Boolean} enableAutocomplete default false
29957  * @cfg {Boolean} enableReverseGeocode default true
29958  * @cfg {String} markerTitle
29959  * 
29960  * @constructor
29961  * Create a new LocationPicker
29962  * @param {Object} config The config object
29963  */
29964
29965
29966 Roo.bootstrap.LocationPicker = function(config){
29967     
29968     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29969     
29970     this.addEvents({
29971         /**
29972          * @event initial
29973          * Fires when the picker initialized.
29974          * @param {Roo.bootstrap.LocationPicker} this
29975          * @param {Google Location} location
29976          */
29977         initial : true,
29978         /**
29979          * @event positionchanged
29980          * Fires when the picker position changed.
29981          * @param {Roo.bootstrap.LocationPicker} this
29982          * @param {Google Location} location
29983          */
29984         positionchanged : true,
29985         /**
29986          * @event resize
29987          * Fires when the map resize.
29988          * @param {Roo.bootstrap.LocationPicker} this
29989          */
29990         resize : true,
29991         /**
29992          * @event show
29993          * Fires when the map show.
29994          * @param {Roo.bootstrap.LocationPicker} this
29995          */
29996         show : true,
29997         /**
29998          * @event hide
29999          * Fires when the map hide.
30000          * @param {Roo.bootstrap.LocationPicker} this
30001          */
30002         hide : true,
30003         /**
30004          * @event mapClick
30005          * Fires when click the map.
30006          * @param {Roo.bootstrap.LocationPicker} this
30007          * @param {Map event} e
30008          */
30009         mapClick : true,
30010         /**
30011          * @event mapRightClick
30012          * Fires when right click the map.
30013          * @param {Roo.bootstrap.LocationPicker} this
30014          * @param {Map event} e
30015          */
30016         mapRightClick : true,
30017         /**
30018          * @event markerClick
30019          * Fires when click the marker.
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          * @param {Map event} e
30022          */
30023         markerClick : true,
30024         /**
30025          * @event markerRightClick
30026          * Fires when right click the marker.
30027          * @param {Roo.bootstrap.LocationPicker} this
30028          * @param {Map event} e
30029          */
30030         markerRightClick : true,
30031         /**
30032          * @event OverlayViewDraw
30033          * Fires when OverlayView Draw
30034          * @param {Roo.bootstrap.LocationPicker} this
30035          */
30036         OverlayViewDraw : true,
30037         /**
30038          * @event OverlayViewOnAdd
30039          * Fires when OverlayView Draw
30040          * @param {Roo.bootstrap.LocationPicker} this
30041          */
30042         OverlayViewOnAdd : true,
30043         /**
30044          * @event OverlayViewOnRemove
30045          * Fires when OverlayView Draw
30046          * @param {Roo.bootstrap.LocationPicker} this
30047          */
30048         OverlayViewOnRemove : true,
30049         /**
30050          * @event OverlayViewShow
30051          * Fires when OverlayView Draw
30052          * @param {Roo.bootstrap.LocationPicker} this
30053          * @param {Pixel} cpx
30054          */
30055         OverlayViewShow : true,
30056         /**
30057          * @event OverlayViewHide
30058          * Fires when OverlayView Draw
30059          * @param {Roo.bootstrap.LocationPicker} this
30060          */
30061         OverlayViewHide : true,
30062         /**
30063          * @event loadexception
30064          * Fires when load google lib failed.
30065          * @param {Roo.bootstrap.LocationPicker} this
30066          */
30067         loadexception : true
30068     });
30069         
30070 };
30071
30072 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30073     
30074     gMapContext: false,
30075     
30076     latitude: 0,
30077     longitude: 0,
30078     zoom: 15,
30079     mapTypeId: false,
30080     mapTypeControl: false,
30081     disableDoubleClickZoom: false,
30082     scrollwheel: true,
30083     streetViewControl: false,
30084     radius: 0,
30085     locationName: '',
30086     draggable: true,
30087     enableAutocomplete: false,
30088     enableReverseGeocode: true,
30089     markerTitle: '',
30090     
30091     getAutoCreate: function()
30092     {
30093
30094         var cfg = {
30095             tag: 'div',
30096             cls: 'roo-location-picker'
30097         };
30098         
30099         return cfg
30100     },
30101     
30102     initEvents: function(ct, position)
30103     {       
30104         if(!this.el.getWidth() || this.isApplied()){
30105             return;
30106         }
30107         
30108         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30109         
30110         this.initial();
30111     },
30112     
30113     initial: function()
30114     {
30115         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30116             this.fireEvent('loadexception', this);
30117             return;
30118         }
30119         
30120         if(!this.mapTypeId){
30121             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30122         }
30123         
30124         this.gMapContext = this.GMapContext();
30125         
30126         this.initOverlayView();
30127         
30128         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30129         
30130         var _this = this;
30131                 
30132         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30133             _this.setPosition(_this.gMapContext.marker.position);
30134         });
30135         
30136         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30137             _this.fireEvent('mapClick', this, event);
30138             
30139         });
30140
30141         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30142             _this.fireEvent('mapRightClick', this, event);
30143             
30144         });
30145         
30146         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30147             _this.fireEvent('markerClick', this, event);
30148             
30149         });
30150
30151         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30152             _this.fireEvent('markerRightClick', this, event);
30153             
30154         });
30155         
30156         this.setPosition(this.gMapContext.location);
30157         
30158         this.fireEvent('initial', this, this.gMapContext.location);
30159     },
30160     
30161     initOverlayView: function()
30162     {
30163         var _this = this;
30164         
30165         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30166             
30167             draw: function()
30168             {
30169                 _this.fireEvent('OverlayViewDraw', _this);
30170             },
30171             
30172             onAdd: function()
30173             {
30174                 _this.fireEvent('OverlayViewOnAdd', _this);
30175             },
30176             
30177             onRemove: function()
30178             {
30179                 _this.fireEvent('OverlayViewOnRemove', _this);
30180             },
30181             
30182             show: function(cpx)
30183             {
30184                 _this.fireEvent('OverlayViewShow', _this, cpx);
30185             },
30186             
30187             hide: function()
30188             {
30189                 _this.fireEvent('OverlayViewHide', _this);
30190             }
30191             
30192         });
30193     },
30194     
30195     fromLatLngToContainerPixel: function(event)
30196     {
30197         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30198     },
30199     
30200     isApplied: function() 
30201     {
30202         return this.getGmapContext() == false ? false : true;
30203     },
30204     
30205     getGmapContext: function() 
30206     {
30207         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30208     },
30209     
30210     GMapContext: function() 
30211     {
30212         var position = new google.maps.LatLng(this.latitude, this.longitude);
30213         
30214         var _map = new google.maps.Map(this.el.dom, {
30215             center: position,
30216             zoom: this.zoom,
30217             mapTypeId: this.mapTypeId,
30218             mapTypeControl: this.mapTypeControl,
30219             disableDoubleClickZoom: this.disableDoubleClickZoom,
30220             scrollwheel: this.scrollwheel,
30221             streetViewControl: this.streetViewControl,
30222             locationName: this.locationName,
30223             draggable: this.draggable,
30224             enableAutocomplete: this.enableAutocomplete,
30225             enableReverseGeocode: this.enableReverseGeocode
30226         });
30227         
30228         var _marker = new google.maps.Marker({
30229             position: position,
30230             map: _map,
30231             title: this.markerTitle,
30232             draggable: this.draggable
30233         });
30234         
30235         return {
30236             map: _map,
30237             marker: _marker,
30238             circle: null,
30239             location: position,
30240             radius: this.radius,
30241             locationName: this.locationName,
30242             addressComponents: {
30243                 formatted_address: null,
30244                 addressLine1: null,
30245                 addressLine2: null,
30246                 streetName: null,
30247                 streetNumber: null,
30248                 city: null,
30249                 district: null,
30250                 state: null,
30251                 stateOrProvince: null
30252             },
30253             settings: this,
30254             domContainer: this.el.dom,
30255             geodecoder: new google.maps.Geocoder()
30256         };
30257     },
30258     
30259     drawCircle: function(center, radius, options) 
30260     {
30261         if (this.gMapContext.circle != null) {
30262             this.gMapContext.circle.setMap(null);
30263         }
30264         if (radius > 0) {
30265             radius *= 1;
30266             options = Roo.apply({}, options, {
30267                 strokeColor: "#0000FF",
30268                 strokeOpacity: .35,
30269                 strokeWeight: 2,
30270                 fillColor: "#0000FF",
30271                 fillOpacity: .2
30272             });
30273             
30274             options.map = this.gMapContext.map;
30275             options.radius = radius;
30276             options.center = center;
30277             this.gMapContext.circle = new google.maps.Circle(options);
30278             return this.gMapContext.circle;
30279         }
30280         
30281         return null;
30282     },
30283     
30284     setPosition: function(location) 
30285     {
30286         this.gMapContext.location = location;
30287         this.gMapContext.marker.setPosition(location);
30288         this.gMapContext.map.panTo(location);
30289         this.drawCircle(location, this.gMapContext.radius, {});
30290         
30291         var _this = this;
30292         
30293         if (this.gMapContext.settings.enableReverseGeocode) {
30294             this.gMapContext.geodecoder.geocode({
30295                 latLng: this.gMapContext.location
30296             }, function(results, status) {
30297                 
30298                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30299                     _this.gMapContext.locationName = results[0].formatted_address;
30300                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30301                     
30302                     _this.fireEvent('positionchanged', this, location);
30303                 }
30304             });
30305             
30306             return;
30307         }
30308         
30309         this.fireEvent('positionchanged', this, location);
30310     },
30311     
30312     resize: function()
30313     {
30314         google.maps.event.trigger(this.gMapContext.map, "resize");
30315         
30316         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30317         
30318         this.fireEvent('resize', this);
30319     },
30320     
30321     setPositionByLatLng: function(latitude, longitude)
30322     {
30323         this.setPosition(new google.maps.LatLng(latitude, longitude));
30324     },
30325     
30326     getCurrentPosition: function() 
30327     {
30328         return {
30329             latitude: this.gMapContext.location.lat(),
30330             longitude: this.gMapContext.location.lng()
30331         };
30332     },
30333     
30334     getAddressName: function() 
30335     {
30336         return this.gMapContext.locationName;
30337     },
30338     
30339     getAddressComponents: function() 
30340     {
30341         return this.gMapContext.addressComponents;
30342     },
30343     
30344     address_component_from_google_geocode: function(address_components) 
30345     {
30346         var result = {};
30347         
30348         for (var i = 0; i < address_components.length; i++) {
30349             var component = address_components[i];
30350             if (component.types.indexOf("postal_code") >= 0) {
30351                 result.postalCode = component.short_name;
30352             } else if (component.types.indexOf("street_number") >= 0) {
30353                 result.streetNumber = component.short_name;
30354             } else if (component.types.indexOf("route") >= 0) {
30355                 result.streetName = component.short_name;
30356             } else if (component.types.indexOf("neighborhood") >= 0) {
30357                 result.city = component.short_name;
30358             } else if (component.types.indexOf("locality") >= 0) {
30359                 result.city = component.short_name;
30360             } else if (component.types.indexOf("sublocality") >= 0) {
30361                 result.district = component.short_name;
30362             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30363                 result.stateOrProvince = component.short_name;
30364             } else if (component.types.indexOf("country") >= 0) {
30365                 result.country = component.short_name;
30366             }
30367         }
30368         
30369         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30370         result.addressLine2 = "";
30371         return result;
30372     },
30373     
30374     setZoomLevel: function(zoom)
30375     {
30376         this.gMapContext.map.setZoom(zoom);
30377     },
30378     
30379     show: function()
30380     {
30381         if(!this.el){
30382             return;
30383         }
30384         
30385         this.el.show();
30386         
30387         this.resize();
30388         
30389         this.fireEvent('show', this);
30390     },
30391     
30392     hide: function()
30393     {
30394         if(!this.el){
30395             return;
30396         }
30397         
30398         this.el.hide();
30399         
30400         this.fireEvent('hide', this);
30401     }
30402     
30403 });
30404
30405 Roo.apply(Roo.bootstrap.LocationPicker, {
30406     
30407     OverlayView : function(map, options)
30408     {
30409         options = options || {};
30410         
30411         this.setMap(map);
30412     }
30413     
30414     
30415 });/**
30416  * @class Roo.bootstrap.Alert
30417  * @extends Roo.bootstrap.Component
30418  * Bootstrap Alert class - shows an alert area box
30419  * eg
30420  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30421   Enter a valid email address
30422 </div>
30423  * @licence LGPL
30424  * @cfg {String} title The title of alert
30425  * @cfg {String} html The content of alert
30426  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30427  * @cfg {String} fa font-awesomeicon
30428  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30429  * @cfg {Boolean} close true to show a x closer
30430  * 
30431  * 
30432  * @constructor
30433  * Create a new alert
30434  * @param {Object} config The config object
30435  */
30436
30437
30438 Roo.bootstrap.Alert = function(config){
30439     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30440     
30441 };
30442
30443 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30444     
30445     title: '',
30446     html: '',
30447     weight: false,
30448     fa: false,
30449     faicon: false, // BC
30450     close : false,
30451     
30452     
30453     getAutoCreate : function()
30454     {
30455         
30456         var cfg = {
30457             tag : 'div',
30458             cls : 'alert',
30459             cn : [
30460                 {
30461                     tag: 'button',
30462                     type :  "button",
30463                     cls: "close",
30464                     html : '×',
30465                     style : this.close ? '' : 'display:none'
30466                 },
30467                 {
30468                     tag : 'i',
30469                     cls : 'roo-alert-icon'
30470                     
30471                 },
30472                 {
30473                     tag : 'b',
30474                     cls : 'roo-alert-title',
30475                     html : this.title
30476                 },
30477                 {
30478                     tag : 'span',
30479                     cls : 'roo-alert-text',
30480                     html : this.html
30481                 }
30482             ]
30483         };
30484         
30485         if(this.faicon){
30486             cfg.cn[0].cls += ' fa ' + this.faicon;
30487         }
30488         if(this.fa){
30489             cfg.cn[0].cls += ' fa ' + this.fa;
30490         }
30491         
30492         if(this.weight){
30493             cfg.cls += ' alert-' + this.weight;
30494         }
30495         
30496         return cfg;
30497     },
30498     
30499     initEvents: function() 
30500     {
30501         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30502         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30503         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30504         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30505         if (this.seconds > 0) {
30506             this.hide.defer(this.seconds, this);
30507         }
30508     },
30509     /**
30510      * Set the Title Message HTML
30511      * @param {String} html
30512      */
30513     setTitle : function(str)
30514     {
30515         this.titleEl.dom.innerHTML = str;
30516     },
30517      
30518      /**
30519      * Set the Body Message HTML
30520      * @param {String} html
30521      */
30522     setHtml : function(str)
30523     {
30524         this.htmlEl.dom.innerHTML = str;
30525     },
30526     /**
30527      * Set the Weight of the alert
30528      * @param {String} (success|info|warning|danger) weight
30529      */
30530     
30531     setWeight : function(weight)
30532     {
30533         if(this.weight){
30534             this.el.removeClass('alert-' + this.weight);
30535         }
30536         
30537         this.weight = weight;
30538         
30539         this.el.addClass('alert-' + this.weight);
30540     },
30541       /**
30542      * Set the Icon of the alert
30543      * @param {String} see fontawsome names (name without the 'fa-' bit)
30544      */
30545     setIcon : function(icon)
30546     {
30547         if(this.faicon){
30548             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30549         }
30550         
30551         this.faicon = icon;
30552         
30553         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30554     },
30555     /**
30556      * Hide the Alert
30557      */
30558     hide: function() 
30559     {
30560         this.el.hide();   
30561     },
30562     /**
30563      * Show the Alert
30564      */
30565     show: function() 
30566     {  
30567         this.el.show();   
30568     }
30569     
30570 });
30571
30572  
30573 /*
30574 * Licence: LGPL
30575 */
30576
30577 /**
30578  * @class Roo.bootstrap.UploadCropbox
30579  * @extends Roo.bootstrap.Component
30580  * Bootstrap UploadCropbox class
30581  * @cfg {String} emptyText show when image has been loaded
30582  * @cfg {String} rotateNotify show when image too small to rotate
30583  * @cfg {Number} errorTimeout default 3000
30584  * @cfg {Number} minWidth default 300
30585  * @cfg {Number} minHeight default 300
30586  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30587  * @cfg {Boolean} isDocument (true|false) default false
30588  * @cfg {String} url action url
30589  * @cfg {String} paramName default 'imageUpload'
30590  * @cfg {String} method default POST
30591  * @cfg {Boolean} loadMask (true|false) default true
30592  * @cfg {Boolean} loadingText default 'Loading...'
30593  * 
30594  * @constructor
30595  * Create a new UploadCropbox
30596  * @param {Object} config The config object
30597  */
30598
30599 Roo.bootstrap.UploadCropbox = function(config){
30600     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30601     
30602     this.addEvents({
30603         /**
30604          * @event beforeselectfile
30605          * Fire before select file
30606          * @param {Roo.bootstrap.UploadCropbox} this
30607          */
30608         "beforeselectfile" : true,
30609         /**
30610          * @event initial
30611          * Fire after initEvent
30612          * @param {Roo.bootstrap.UploadCropbox} this
30613          */
30614         "initial" : true,
30615         /**
30616          * @event crop
30617          * Fire after initEvent
30618          * @param {Roo.bootstrap.UploadCropbox} this
30619          * @param {String} data
30620          */
30621         "crop" : true,
30622         /**
30623          * @event prepare
30624          * Fire when preparing the file data
30625          * @param {Roo.bootstrap.UploadCropbox} this
30626          * @param {Object} file
30627          */
30628         "prepare" : true,
30629         /**
30630          * @event exception
30631          * Fire when get exception
30632          * @param {Roo.bootstrap.UploadCropbox} this
30633          * @param {XMLHttpRequest} xhr
30634          */
30635         "exception" : true,
30636         /**
30637          * @event beforeloadcanvas
30638          * Fire before load the canvas
30639          * @param {Roo.bootstrap.UploadCropbox} this
30640          * @param {String} src
30641          */
30642         "beforeloadcanvas" : true,
30643         /**
30644          * @event trash
30645          * Fire when trash image
30646          * @param {Roo.bootstrap.UploadCropbox} this
30647          */
30648         "trash" : true,
30649         /**
30650          * @event download
30651          * Fire when download the image
30652          * @param {Roo.bootstrap.UploadCropbox} this
30653          */
30654         "download" : true,
30655         /**
30656          * @event footerbuttonclick
30657          * Fire when footerbuttonclick
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          * @param {String} type
30660          */
30661         "footerbuttonclick" : true,
30662         /**
30663          * @event resize
30664          * Fire when resize
30665          * @param {Roo.bootstrap.UploadCropbox} this
30666          */
30667         "resize" : true,
30668         /**
30669          * @event rotate
30670          * Fire when rotate the image
30671          * @param {Roo.bootstrap.UploadCropbox} this
30672          * @param {String} pos
30673          */
30674         "rotate" : true,
30675         /**
30676          * @event inspect
30677          * Fire when inspect the file
30678          * @param {Roo.bootstrap.UploadCropbox} this
30679          * @param {Object} file
30680          */
30681         "inspect" : true,
30682         /**
30683          * @event upload
30684          * Fire when xhr upload the file
30685          * @param {Roo.bootstrap.UploadCropbox} this
30686          * @param {Object} data
30687          */
30688         "upload" : true,
30689         /**
30690          * @event arrange
30691          * Fire when arrange the file data
30692          * @param {Roo.bootstrap.UploadCropbox} this
30693          * @param {Object} formData
30694          */
30695         "arrange" : true
30696     });
30697     
30698     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30699 };
30700
30701 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30702     
30703     emptyText : 'Click to upload image',
30704     rotateNotify : 'Image is too small to rotate',
30705     errorTimeout : 3000,
30706     scale : 0,
30707     baseScale : 1,
30708     rotate : 0,
30709     dragable : false,
30710     pinching : false,
30711     mouseX : 0,
30712     mouseY : 0,
30713     cropData : false,
30714     minWidth : 300,
30715     minHeight : 300,
30716     file : false,
30717     exif : {},
30718     baseRotate : 1,
30719     cropType : 'image/jpeg',
30720     buttons : false,
30721     canvasLoaded : false,
30722     isDocument : false,
30723     method : 'POST',
30724     paramName : 'imageUpload',
30725     loadMask : true,
30726     loadingText : 'Loading...',
30727     maskEl : false,
30728     
30729     getAutoCreate : function()
30730     {
30731         var cfg = {
30732             tag : 'div',
30733             cls : 'roo-upload-cropbox',
30734             cn : [
30735                 {
30736                     tag : 'input',
30737                     cls : 'roo-upload-cropbox-selector',
30738                     type : 'file'
30739                 },
30740                 {
30741                     tag : 'div',
30742                     cls : 'roo-upload-cropbox-body',
30743                     style : 'cursor:pointer',
30744                     cn : [
30745                         {
30746                             tag : 'div',
30747                             cls : 'roo-upload-cropbox-preview'
30748                         },
30749                         {
30750                             tag : 'div',
30751                             cls : 'roo-upload-cropbox-thumb'
30752                         },
30753                         {
30754                             tag : 'div',
30755                             cls : 'roo-upload-cropbox-empty-notify',
30756                             html : this.emptyText
30757                         },
30758                         {
30759                             tag : 'div',
30760                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30761                             html : this.rotateNotify
30762                         }
30763                     ]
30764                 },
30765                 {
30766                     tag : 'div',
30767                     cls : 'roo-upload-cropbox-footer',
30768                     cn : {
30769                         tag : 'div',
30770                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30771                         cn : []
30772                     }
30773                 }
30774             ]
30775         };
30776         
30777         return cfg;
30778     },
30779     
30780     onRender : function(ct, position)
30781     {
30782         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30783         
30784         if (this.buttons.length) {
30785             
30786             Roo.each(this.buttons, function(bb) {
30787                 
30788                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30789                 
30790                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30791                 
30792             }, this);
30793         }
30794         
30795         if(this.loadMask){
30796             this.maskEl = this.el;
30797         }
30798     },
30799     
30800     initEvents : function()
30801     {
30802         this.urlAPI = (window.createObjectURL && window) || 
30803                                 (window.URL && URL.revokeObjectURL && URL) || 
30804                                 (window.webkitURL && webkitURL);
30805                         
30806         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30807         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808         
30809         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30810         this.selectorEl.hide();
30811         
30812         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30813         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30814         
30815         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30816         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30817         this.thumbEl.hide();
30818         
30819         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30820         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30821         
30822         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30823         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30824         this.errorEl.hide();
30825         
30826         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30827         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30828         this.footerEl.hide();
30829         
30830         this.setThumbBoxSize();
30831         
30832         this.bind();
30833         
30834         this.resize();
30835         
30836         this.fireEvent('initial', this);
30837     },
30838
30839     bind : function()
30840     {
30841         var _this = this;
30842         
30843         window.addEventListener("resize", function() { _this.resize(); } );
30844         
30845         this.bodyEl.on('click', this.beforeSelectFile, this);
30846         
30847         if(Roo.isTouch){
30848             this.bodyEl.on('touchstart', this.onTouchStart, this);
30849             this.bodyEl.on('touchmove', this.onTouchMove, this);
30850             this.bodyEl.on('touchend', this.onTouchEnd, this);
30851         }
30852         
30853         if(!Roo.isTouch){
30854             this.bodyEl.on('mousedown', this.onMouseDown, this);
30855             this.bodyEl.on('mousemove', this.onMouseMove, this);
30856             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30857             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30858             Roo.get(document).on('mouseup', this.onMouseUp, this);
30859         }
30860         
30861         this.selectorEl.on('change', this.onFileSelected, this);
30862     },
30863     
30864     reset : function()
30865     {    
30866         this.scale = 0;
30867         this.baseScale = 1;
30868         this.rotate = 0;
30869         this.baseRotate = 1;
30870         this.dragable = false;
30871         this.pinching = false;
30872         this.mouseX = 0;
30873         this.mouseY = 0;
30874         this.cropData = false;
30875         this.notifyEl.dom.innerHTML = this.emptyText;
30876         
30877         this.selectorEl.dom.value = '';
30878         
30879     },
30880     
30881     resize : function()
30882     {
30883         if(this.fireEvent('resize', this) != false){
30884             this.setThumbBoxPosition();
30885             this.setCanvasPosition();
30886         }
30887     },
30888     
30889     onFooterButtonClick : function(e, el, o, type)
30890     {
30891         switch (type) {
30892             case 'rotate-left' :
30893                 this.onRotateLeft(e);
30894                 break;
30895             case 'rotate-right' :
30896                 this.onRotateRight(e);
30897                 break;
30898             case 'picture' :
30899                 this.beforeSelectFile(e);
30900                 break;
30901             case 'trash' :
30902                 this.trash(e);
30903                 break;
30904             case 'crop' :
30905                 this.crop(e);
30906                 break;
30907             case 'download' :
30908                 this.download(e);
30909                 break;
30910             default :
30911                 break;
30912         }
30913         
30914         this.fireEvent('footerbuttonclick', this, type);
30915     },
30916     
30917     beforeSelectFile : function(e)
30918     {
30919         e.preventDefault();
30920         
30921         if(this.fireEvent('beforeselectfile', this) != false){
30922             this.selectorEl.dom.click();
30923         }
30924     },
30925     
30926     onFileSelected : function(e)
30927     {
30928         e.preventDefault();
30929         
30930         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30931             return;
30932         }
30933         
30934         var file = this.selectorEl.dom.files[0];
30935         
30936         if(this.fireEvent('inspect', this, file) != false){
30937             this.prepare(file);
30938         }
30939         
30940     },
30941     
30942     trash : function(e)
30943     {
30944         this.fireEvent('trash', this);
30945     },
30946     
30947     download : function(e)
30948     {
30949         this.fireEvent('download', this);
30950     },
30951     
30952     loadCanvas : function(src)
30953     {   
30954         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30955             
30956             this.reset();
30957             
30958             this.imageEl = document.createElement('img');
30959             
30960             var _this = this;
30961             
30962             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30963             
30964             this.imageEl.src = src;
30965         }
30966     },
30967     
30968     onLoadCanvas : function()
30969     {   
30970         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30971         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30972         
30973         this.bodyEl.un('click', this.beforeSelectFile, this);
30974         
30975         this.notifyEl.hide();
30976         this.thumbEl.show();
30977         this.footerEl.show();
30978         
30979         this.baseRotateLevel();
30980         
30981         if(this.isDocument){
30982             this.setThumbBoxSize();
30983         }
30984         
30985         this.setThumbBoxPosition();
30986         
30987         this.baseScaleLevel();
30988         
30989         this.draw();
30990         
30991         this.resize();
30992         
30993         this.canvasLoaded = true;
30994         
30995         if(this.loadMask){
30996             this.maskEl.unmask();
30997         }
30998         
30999     },
31000     
31001     setCanvasPosition : function()
31002     {   
31003         if(!this.canvasEl){
31004             return;
31005         }
31006         
31007         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31008         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31009         
31010         this.previewEl.setLeft(pw);
31011         this.previewEl.setTop(ph);
31012         
31013     },
31014     
31015     onMouseDown : function(e)
31016     {   
31017         e.stopEvent();
31018         
31019         this.dragable = true;
31020         this.pinching = false;
31021         
31022         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31023             this.dragable = false;
31024             return;
31025         }
31026         
31027         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31028         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31029         
31030     },
31031     
31032     onMouseMove : function(e)
31033     {   
31034         e.stopEvent();
31035         
31036         if(!this.canvasLoaded){
31037             return;
31038         }
31039         
31040         if (!this.dragable){
31041             return;
31042         }
31043         
31044         var minX = Math.ceil(this.thumbEl.getLeft(true));
31045         var minY = Math.ceil(this.thumbEl.getTop(true));
31046         
31047         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31048         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31049         
31050         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31051         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31052         
31053         x = x - this.mouseX;
31054         y = y - this.mouseY;
31055         
31056         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31057         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31058         
31059         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31060         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31061         
31062         this.previewEl.setLeft(bgX);
31063         this.previewEl.setTop(bgY);
31064         
31065         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31066         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31067     },
31068     
31069     onMouseUp : function(e)
31070     {   
31071         e.stopEvent();
31072         
31073         this.dragable = false;
31074     },
31075     
31076     onMouseWheel : function(e)
31077     {   
31078         e.stopEvent();
31079         
31080         this.startScale = this.scale;
31081         
31082         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31083         
31084         if(!this.zoomable()){
31085             this.scale = this.startScale;
31086             return;
31087         }
31088         
31089         this.draw();
31090         
31091         return;
31092     },
31093     
31094     zoomable : function()
31095     {
31096         var minScale = this.thumbEl.getWidth() / this.minWidth;
31097         
31098         if(this.minWidth < this.minHeight){
31099             minScale = this.thumbEl.getHeight() / this.minHeight;
31100         }
31101         
31102         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31103         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31104         
31105         if(
31106                 this.isDocument &&
31107                 (this.rotate == 0 || this.rotate == 180) && 
31108                 (
31109                     width > this.imageEl.OriginWidth || 
31110                     height > this.imageEl.OriginHeight ||
31111                     (width < this.minWidth && height < this.minHeight)
31112                 )
31113         ){
31114             return false;
31115         }
31116         
31117         if(
31118                 this.isDocument &&
31119                 (this.rotate == 90 || this.rotate == 270) && 
31120                 (
31121                     width > this.imageEl.OriginWidth || 
31122                     height > this.imageEl.OriginHeight ||
31123                     (width < this.minHeight && height < this.minWidth)
31124                 )
31125         ){
31126             return false;
31127         }
31128         
31129         if(
31130                 !this.isDocument &&
31131                 (this.rotate == 0 || this.rotate == 180) && 
31132                 (
31133                     width < this.minWidth || 
31134                     width > this.imageEl.OriginWidth || 
31135                     height < this.minHeight || 
31136                     height > this.imageEl.OriginHeight
31137                 )
31138         ){
31139             return false;
31140         }
31141         
31142         if(
31143                 !this.isDocument &&
31144                 (this.rotate == 90 || this.rotate == 270) && 
31145                 (
31146                     width < this.minHeight || 
31147                     width > this.imageEl.OriginWidth || 
31148                     height < this.minWidth || 
31149                     height > this.imageEl.OriginHeight
31150                 )
31151         ){
31152             return false;
31153         }
31154         
31155         return true;
31156         
31157     },
31158     
31159     onRotateLeft : function(e)
31160     {   
31161         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31162             
31163             var minScale = this.thumbEl.getWidth() / this.minWidth;
31164             
31165             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31166             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31167             
31168             this.startScale = this.scale;
31169             
31170             while (this.getScaleLevel() < minScale){
31171             
31172                 this.scale = this.scale + 1;
31173                 
31174                 if(!this.zoomable()){
31175                     break;
31176                 }
31177                 
31178                 if(
31179                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31180                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31181                 ){
31182                     continue;
31183                 }
31184                 
31185                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31186
31187                 this.draw();
31188                 
31189                 return;
31190             }
31191             
31192             this.scale = this.startScale;
31193             
31194             this.onRotateFail();
31195             
31196             return false;
31197         }
31198         
31199         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31200
31201         if(this.isDocument){
31202             this.setThumbBoxSize();
31203             this.setThumbBoxPosition();
31204             this.setCanvasPosition();
31205         }
31206         
31207         this.draw();
31208         
31209         this.fireEvent('rotate', this, 'left');
31210         
31211     },
31212     
31213     onRotateRight : function(e)
31214     {
31215         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31216             
31217             var minScale = this.thumbEl.getWidth() / this.minWidth;
31218         
31219             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31220             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31221             
31222             this.startScale = this.scale;
31223             
31224             while (this.getScaleLevel() < minScale){
31225             
31226                 this.scale = this.scale + 1;
31227                 
31228                 if(!this.zoomable()){
31229                     break;
31230                 }
31231                 
31232                 if(
31233                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31234                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31235                 ){
31236                     continue;
31237                 }
31238                 
31239                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31240
31241                 this.draw();
31242                 
31243                 return;
31244             }
31245             
31246             this.scale = this.startScale;
31247             
31248             this.onRotateFail();
31249             
31250             return false;
31251         }
31252         
31253         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31254
31255         if(this.isDocument){
31256             this.setThumbBoxSize();
31257             this.setThumbBoxPosition();
31258             this.setCanvasPosition();
31259         }
31260         
31261         this.draw();
31262         
31263         this.fireEvent('rotate', this, 'right');
31264     },
31265     
31266     onRotateFail : function()
31267     {
31268         this.errorEl.show(true);
31269         
31270         var _this = this;
31271         
31272         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31273     },
31274     
31275     draw : function()
31276     {
31277         this.previewEl.dom.innerHTML = '';
31278         
31279         var canvasEl = document.createElement("canvas");
31280         
31281         var contextEl = canvasEl.getContext("2d");
31282         
31283         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31284         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31285         var center = this.imageEl.OriginWidth / 2;
31286         
31287         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31288             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31289             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31290             center = this.imageEl.OriginHeight / 2;
31291         }
31292         
31293         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31294         
31295         contextEl.translate(center, center);
31296         contextEl.rotate(this.rotate * Math.PI / 180);
31297
31298         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31299         
31300         this.canvasEl = document.createElement("canvas");
31301         
31302         this.contextEl = this.canvasEl.getContext("2d");
31303         
31304         switch (this.rotate) {
31305             case 0 :
31306                 
31307                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31308                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31309                 
31310                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31311                 
31312                 break;
31313             case 90 : 
31314                 
31315                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31316                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31317                 
31318                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31319                     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);
31320                     break;
31321                 }
31322                 
31323                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31324                 
31325                 break;
31326             case 180 :
31327                 
31328                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31329                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31330                 
31331                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31332                     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);
31333                     break;
31334                 }
31335                 
31336                 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);
31337                 
31338                 break;
31339             case 270 :
31340                 
31341                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31342                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31343         
31344                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31345                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31346                     break;
31347                 }
31348                 
31349                 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);
31350                 
31351                 break;
31352             default : 
31353                 break;
31354         }
31355         
31356         this.previewEl.appendChild(this.canvasEl);
31357         
31358         this.setCanvasPosition();
31359     },
31360     
31361     crop : function()
31362     {
31363         if(!this.canvasLoaded){
31364             return;
31365         }
31366         
31367         var imageCanvas = document.createElement("canvas");
31368         
31369         var imageContext = imageCanvas.getContext("2d");
31370         
31371         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31372         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31373         
31374         var center = imageCanvas.width / 2;
31375         
31376         imageContext.translate(center, center);
31377         
31378         imageContext.rotate(this.rotate * Math.PI / 180);
31379         
31380         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31381         
31382         var canvas = document.createElement("canvas");
31383         
31384         var context = canvas.getContext("2d");
31385                 
31386         canvas.width = this.minWidth;
31387         canvas.height = this.minHeight;
31388
31389         switch (this.rotate) {
31390             case 0 :
31391                 
31392                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31393                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31394                 
31395                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31396                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31397                 
31398                 var targetWidth = this.minWidth - 2 * x;
31399                 var targetHeight = this.minHeight - 2 * y;
31400                 
31401                 var scale = 1;
31402                 
31403                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31404                     scale = targetWidth / width;
31405                 }
31406                 
31407                 if(x > 0 && y == 0){
31408                     scale = targetHeight / height;
31409                 }
31410                 
31411                 if(x > 0 && y > 0){
31412                     scale = targetWidth / width;
31413                     
31414                     if(width < height){
31415                         scale = targetHeight / height;
31416                     }
31417                 }
31418                 
31419                 context.scale(scale, scale);
31420                 
31421                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31422                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31423
31424                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31425                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31426
31427                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31428                 
31429                 break;
31430             case 90 : 
31431                 
31432                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31433                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31434                 
31435                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31436                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31437                 
31438                 var targetWidth = this.minWidth - 2 * x;
31439                 var targetHeight = this.minHeight - 2 * y;
31440                 
31441                 var scale = 1;
31442                 
31443                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31444                     scale = targetWidth / width;
31445                 }
31446                 
31447                 if(x > 0 && y == 0){
31448                     scale = targetHeight / height;
31449                 }
31450                 
31451                 if(x > 0 && y > 0){
31452                     scale = targetWidth / width;
31453                     
31454                     if(width < height){
31455                         scale = targetHeight / height;
31456                     }
31457                 }
31458                 
31459                 context.scale(scale, scale);
31460                 
31461                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31462                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31463
31464                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31465                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31466                 
31467                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31468                 
31469                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31470                 
31471                 break;
31472             case 180 :
31473                 
31474                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31475                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31476                 
31477                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31478                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31479                 
31480                 var targetWidth = this.minWidth - 2 * x;
31481                 var targetHeight = this.minHeight - 2 * y;
31482                 
31483                 var scale = 1;
31484                 
31485                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31486                     scale = targetWidth / width;
31487                 }
31488                 
31489                 if(x > 0 && y == 0){
31490                     scale = targetHeight / height;
31491                 }
31492                 
31493                 if(x > 0 && y > 0){
31494                     scale = targetWidth / width;
31495                     
31496                     if(width < height){
31497                         scale = targetHeight / height;
31498                     }
31499                 }
31500                 
31501                 context.scale(scale, scale);
31502                 
31503                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31504                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31505
31506                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31507                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31508
31509                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31510                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31511                 
31512                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31513                 
31514                 break;
31515             case 270 :
31516                 
31517                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31518                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31519                 
31520                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31521                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31522                 
31523                 var targetWidth = this.minWidth - 2 * x;
31524                 var targetHeight = this.minHeight - 2 * y;
31525                 
31526                 var scale = 1;
31527                 
31528                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31529                     scale = targetWidth / width;
31530                 }
31531                 
31532                 if(x > 0 && y == 0){
31533                     scale = targetHeight / height;
31534                 }
31535                 
31536                 if(x > 0 && y > 0){
31537                     scale = targetWidth / width;
31538                     
31539                     if(width < height){
31540                         scale = targetHeight / height;
31541                     }
31542                 }
31543                 
31544                 context.scale(scale, scale);
31545                 
31546                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31547                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31548
31549                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31550                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31551                 
31552                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31553                 
31554                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31555                 
31556                 break;
31557             default : 
31558                 break;
31559         }
31560         
31561         this.cropData = canvas.toDataURL(this.cropType);
31562         
31563         if(this.fireEvent('crop', this, this.cropData) !== false){
31564             this.process(this.file, this.cropData);
31565         }
31566         
31567         return;
31568         
31569     },
31570     
31571     setThumbBoxSize : function()
31572     {
31573         var width, height;
31574         
31575         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31576             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31577             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31578             
31579             this.minWidth = width;
31580             this.minHeight = height;
31581             
31582             if(this.rotate == 90 || this.rotate == 270){
31583                 this.minWidth = height;
31584                 this.minHeight = width;
31585             }
31586         }
31587         
31588         height = 300;
31589         width = Math.ceil(this.minWidth * height / this.minHeight);
31590         
31591         if(this.minWidth > this.minHeight){
31592             width = 300;
31593             height = Math.ceil(this.minHeight * width / this.minWidth);
31594         }
31595         
31596         this.thumbEl.setStyle({
31597             width : width + 'px',
31598             height : height + 'px'
31599         });
31600
31601         return;
31602             
31603     },
31604     
31605     setThumbBoxPosition : function()
31606     {
31607         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31608         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31609         
31610         this.thumbEl.setLeft(x);
31611         this.thumbEl.setTop(y);
31612         
31613     },
31614     
31615     baseRotateLevel : function()
31616     {
31617         this.baseRotate = 1;
31618         
31619         if(
31620                 typeof(this.exif) != 'undefined' &&
31621                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31622                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31623         ){
31624             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31625         }
31626         
31627         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31628         
31629     },
31630     
31631     baseScaleLevel : function()
31632     {
31633         var width, height;
31634         
31635         if(this.isDocument){
31636             
31637             if(this.baseRotate == 6 || this.baseRotate == 8){
31638             
31639                 height = this.thumbEl.getHeight();
31640                 this.baseScale = height / this.imageEl.OriginWidth;
31641
31642                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31643                     width = this.thumbEl.getWidth();
31644                     this.baseScale = width / this.imageEl.OriginHeight;
31645                 }
31646
31647                 return;
31648             }
31649
31650             height = this.thumbEl.getHeight();
31651             this.baseScale = height / this.imageEl.OriginHeight;
31652
31653             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31654                 width = this.thumbEl.getWidth();
31655                 this.baseScale = width / this.imageEl.OriginWidth;
31656             }
31657
31658             return;
31659         }
31660         
31661         if(this.baseRotate == 6 || this.baseRotate == 8){
31662             
31663             width = this.thumbEl.getHeight();
31664             this.baseScale = width / this.imageEl.OriginHeight;
31665             
31666             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31667                 height = this.thumbEl.getWidth();
31668                 this.baseScale = height / this.imageEl.OriginHeight;
31669             }
31670             
31671             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31672                 height = this.thumbEl.getWidth();
31673                 this.baseScale = height / this.imageEl.OriginHeight;
31674                 
31675                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31676                     width = this.thumbEl.getHeight();
31677                     this.baseScale = width / this.imageEl.OriginWidth;
31678                 }
31679             }
31680             
31681             return;
31682         }
31683         
31684         width = this.thumbEl.getWidth();
31685         this.baseScale = width / this.imageEl.OriginWidth;
31686         
31687         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31688             height = this.thumbEl.getHeight();
31689             this.baseScale = height / this.imageEl.OriginHeight;
31690         }
31691         
31692         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31693             
31694             height = this.thumbEl.getHeight();
31695             this.baseScale = height / this.imageEl.OriginHeight;
31696             
31697             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31698                 width = this.thumbEl.getWidth();
31699                 this.baseScale = width / this.imageEl.OriginWidth;
31700             }
31701             
31702         }
31703         
31704         return;
31705     },
31706     
31707     getScaleLevel : function()
31708     {
31709         return this.baseScale * Math.pow(1.1, this.scale);
31710     },
31711     
31712     onTouchStart : function(e)
31713     {
31714         if(!this.canvasLoaded){
31715             this.beforeSelectFile(e);
31716             return;
31717         }
31718         
31719         var touches = e.browserEvent.touches;
31720         
31721         if(!touches){
31722             return;
31723         }
31724         
31725         if(touches.length == 1){
31726             this.onMouseDown(e);
31727             return;
31728         }
31729         
31730         if(touches.length != 2){
31731             return;
31732         }
31733         
31734         var coords = [];
31735         
31736         for(var i = 0, finger; finger = touches[i]; i++){
31737             coords.push(finger.pageX, finger.pageY);
31738         }
31739         
31740         var x = Math.pow(coords[0] - coords[2], 2);
31741         var y = Math.pow(coords[1] - coords[3], 2);
31742         
31743         this.startDistance = Math.sqrt(x + y);
31744         
31745         this.startScale = this.scale;
31746         
31747         this.pinching = true;
31748         this.dragable = false;
31749         
31750     },
31751     
31752     onTouchMove : function(e)
31753     {
31754         if(!this.pinching && !this.dragable){
31755             return;
31756         }
31757         
31758         var touches = e.browserEvent.touches;
31759         
31760         if(!touches){
31761             return;
31762         }
31763         
31764         if(this.dragable){
31765             this.onMouseMove(e);
31766             return;
31767         }
31768         
31769         var coords = [];
31770         
31771         for(var i = 0, finger; finger = touches[i]; i++){
31772             coords.push(finger.pageX, finger.pageY);
31773         }
31774         
31775         var x = Math.pow(coords[0] - coords[2], 2);
31776         var y = Math.pow(coords[1] - coords[3], 2);
31777         
31778         this.endDistance = Math.sqrt(x + y);
31779         
31780         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31781         
31782         if(!this.zoomable()){
31783             this.scale = this.startScale;
31784             return;
31785         }
31786         
31787         this.draw();
31788         
31789     },
31790     
31791     onTouchEnd : function(e)
31792     {
31793         this.pinching = false;
31794         this.dragable = false;
31795         
31796     },
31797     
31798     process : function(file, crop)
31799     {
31800         if(this.loadMask){
31801             this.maskEl.mask(this.loadingText);
31802         }
31803         
31804         this.xhr = new XMLHttpRequest();
31805         
31806         file.xhr = this.xhr;
31807
31808         this.xhr.open(this.method, this.url, true);
31809         
31810         var headers = {
31811             "Accept": "application/json",
31812             "Cache-Control": "no-cache",
31813             "X-Requested-With": "XMLHttpRequest"
31814         };
31815         
31816         for (var headerName in headers) {
31817             var headerValue = headers[headerName];
31818             if (headerValue) {
31819                 this.xhr.setRequestHeader(headerName, headerValue);
31820             }
31821         }
31822         
31823         var _this = this;
31824         
31825         this.xhr.onload = function()
31826         {
31827             _this.xhrOnLoad(_this.xhr);
31828         }
31829         
31830         this.xhr.onerror = function()
31831         {
31832             _this.xhrOnError(_this.xhr);
31833         }
31834         
31835         var formData = new FormData();
31836
31837         formData.append('returnHTML', 'NO');
31838         
31839         if(crop){
31840             formData.append('crop', crop);
31841         }
31842         
31843         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31844             formData.append(this.paramName, file, file.name);
31845         }
31846         
31847         if(typeof(file.filename) != 'undefined'){
31848             formData.append('filename', file.filename);
31849         }
31850         
31851         if(typeof(file.mimetype) != 'undefined'){
31852             formData.append('mimetype', file.mimetype);
31853         }
31854         
31855         if(this.fireEvent('arrange', this, formData) != false){
31856             this.xhr.send(formData);
31857         };
31858     },
31859     
31860     xhrOnLoad : function(xhr)
31861     {
31862         if(this.loadMask){
31863             this.maskEl.unmask();
31864         }
31865         
31866         if (xhr.readyState !== 4) {
31867             this.fireEvent('exception', this, xhr);
31868             return;
31869         }
31870
31871         var response = Roo.decode(xhr.responseText);
31872         
31873         if(!response.success){
31874             this.fireEvent('exception', this, xhr);
31875             return;
31876         }
31877         
31878         var response = Roo.decode(xhr.responseText);
31879         
31880         this.fireEvent('upload', this, response);
31881         
31882     },
31883     
31884     xhrOnError : function()
31885     {
31886         if(this.loadMask){
31887             this.maskEl.unmask();
31888         }
31889         
31890         Roo.log('xhr on error');
31891         
31892         var response = Roo.decode(xhr.responseText);
31893           
31894         Roo.log(response);
31895         
31896     },
31897     
31898     prepare : function(file)
31899     {   
31900         if(this.loadMask){
31901             this.maskEl.mask(this.loadingText);
31902         }
31903         
31904         this.file = false;
31905         this.exif = {};
31906         
31907         if(typeof(file) === 'string'){
31908             this.loadCanvas(file);
31909             return;
31910         }
31911         
31912         if(!file || !this.urlAPI){
31913             return;
31914         }
31915         
31916         this.file = file;
31917         this.cropType = file.type;
31918         
31919         var _this = this;
31920         
31921         if(this.fireEvent('prepare', this, this.file) != false){
31922             
31923             var reader = new FileReader();
31924             
31925             reader.onload = function (e) {
31926                 if (e.target.error) {
31927                     Roo.log(e.target.error);
31928                     return;
31929                 }
31930                 
31931                 var buffer = e.target.result,
31932                     dataView = new DataView(buffer),
31933                     offset = 2,
31934                     maxOffset = dataView.byteLength - 4,
31935                     markerBytes,
31936                     markerLength;
31937                 
31938                 if (dataView.getUint16(0) === 0xffd8) {
31939                     while (offset < maxOffset) {
31940                         markerBytes = dataView.getUint16(offset);
31941                         
31942                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31943                             markerLength = dataView.getUint16(offset + 2) + 2;
31944                             if (offset + markerLength > dataView.byteLength) {
31945                                 Roo.log('Invalid meta data: Invalid segment size.');
31946                                 break;
31947                             }
31948                             
31949                             if(markerBytes == 0xffe1){
31950                                 _this.parseExifData(
31951                                     dataView,
31952                                     offset,
31953                                     markerLength
31954                                 );
31955                             }
31956                             
31957                             offset += markerLength;
31958                             
31959                             continue;
31960                         }
31961                         
31962                         break;
31963                     }
31964                     
31965                 }
31966                 
31967                 var url = _this.urlAPI.createObjectURL(_this.file);
31968                 
31969                 _this.loadCanvas(url);
31970                 
31971                 return;
31972             }
31973             
31974             reader.readAsArrayBuffer(this.file);
31975             
31976         }
31977         
31978     },
31979     
31980     parseExifData : function(dataView, offset, length)
31981     {
31982         var tiffOffset = offset + 10,
31983             littleEndian,
31984             dirOffset;
31985     
31986         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31987             // No Exif data, might be XMP data instead
31988             return;
31989         }
31990         
31991         // Check for the ASCII code for "Exif" (0x45786966):
31992         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31993             // No Exif data, might be XMP data instead
31994             return;
31995         }
31996         if (tiffOffset + 8 > dataView.byteLength) {
31997             Roo.log('Invalid Exif data: Invalid segment size.');
31998             return;
31999         }
32000         // Check for the two null bytes:
32001         if (dataView.getUint16(offset + 8) !== 0x0000) {
32002             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32003             return;
32004         }
32005         // Check the byte alignment:
32006         switch (dataView.getUint16(tiffOffset)) {
32007         case 0x4949:
32008             littleEndian = true;
32009             break;
32010         case 0x4D4D:
32011             littleEndian = false;
32012             break;
32013         default:
32014             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32015             return;
32016         }
32017         // Check for the TIFF tag marker (0x002A):
32018         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32019             Roo.log('Invalid Exif data: Missing TIFF marker.');
32020             return;
32021         }
32022         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32023         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32024         
32025         this.parseExifTags(
32026             dataView,
32027             tiffOffset,
32028             tiffOffset + dirOffset,
32029             littleEndian
32030         );
32031     },
32032     
32033     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32034     {
32035         var tagsNumber,
32036             dirEndOffset,
32037             i;
32038         if (dirOffset + 6 > dataView.byteLength) {
32039             Roo.log('Invalid Exif data: Invalid directory offset.');
32040             return;
32041         }
32042         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32043         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32044         if (dirEndOffset + 4 > dataView.byteLength) {
32045             Roo.log('Invalid Exif data: Invalid directory size.');
32046             return;
32047         }
32048         for (i = 0; i < tagsNumber; i += 1) {
32049             this.parseExifTag(
32050                 dataView,
32051                 tiffOffset,
32052                 dirOffset + 2 + 12 * i, // tag offset
32053                 littleEndian
32054             );
32055         }
32056         // Return the offset to the next directory:
32057         return dataView.getUint32(dirEndOffset, littleEndian);
32058     },
32059     
32060     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32061     {
32062         var tag = dataView.getUint16(offset, littleEndian);
32063         
32064         this.exif[tag] = this.getExifValue(
32065             dataView,
32066             tiffOffset,
32067             offset,
32068             dataView.getUint16(offset + 2, littleEndian), // tag type
32069             dataView.getUint32(offset + 4, littleEndian), // tag length
32070             littleEndian
32071         );
32072     },
32073     
32074     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32075     {
32076         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32077             tagSize,
32078             dataOffset,
32079             values,
32080             i,
32081             str,
32082             c;
32083     
32084         if (!tagType) {
32085             Roo.log('Invalid Exif data: Invalid tag type.');
32086             return;
32087         }
32088         
32089         tagSize = tagType.size * length;
32090         // Determine if the value is contained in the dataOffset bytes,
32091         // or if the value at the dataOffset is a pointer to the actual data:
32092         dataOffset = tagSize > 4 ?
32093                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32094         if (dataOffset + tagSize > dataView.byteLength) {
32095             Roo.log('Invalid Exif data: Invalid data offset.');
32096             return;
32097         }
32098         if (length === 1) {
32099             return tagType.getValue(dataView, dataOffset, littleEndian);
32100         }
32101         values = [];
32102         for (i = 0; i < length; i += 1) {
32103             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32104         }
32105         
32106         if (tagType.ascii) {
32107             str = '';
32108             // Concatenate the chars:
32109             for (i = 0; i < values.length; i += 1) {
32110                 c = values[i];
32111                 // Ignore the terminating NULL byte(s):
32112                 if (c === '\u0000') {
32113                     break;
32114                 }
32115                 str += c;
32116             }
32117             return str;
32118         }
32119         return values;
32120     }
32121     
32122 });
32123
32124 Roo.apply(Roo.bootstrap.UploadCropbox, {
32125     tags : {
32126         'Orientation': 0x0112
32127     },
32128     
32129     Orientation: {
32130             1: 0, //'top-left',
32131 //            2: 'top-right',
32132             3: 180, //'bottom-right',
32133 //            4: 'bottom-left',
32134 //            5: 'left-top',
32135             6: 90, //'right-top',
32136 //            7: 'right-bottom',
32137             8: 270 //'left-bottom'
32138     },
32139     
32140     exifTagTypes : {
32141         // byte, 8-bit unsigned int:
32142         1: {
32143             getValue: function (dataView, dataOffset) {
32144                 return dataView.getUint8(dataOffset);
32145             },
32146             size: 1
32147         },
32148         // ascii, 8-bit byte:
32149         2: {
32150             getValue: function (dataView, dataOffset) {
32151                 return String.fromCharCode(dataView.getUint8(dataOffset));
32152             },
32153             size: 1,
32154             ascii: true
32155         },
32156         // short, 16 bit int:
32157         3: {
32158             getValue: function (dataView, dataOffset, littleEndian) {
32159                 return dataView.getUint16(dataOffset, littleEndian);
32160             },
32161             size: 2
32162         },
32163         // long, 32 bit int:
32164         4: {
32165             getValue: function (dataView, dataOffset, littleEndian) {
32166                 return dataView.getUint32(dataOffset, littleEndian);
32167             },
32168             size: 4
32169         },
32170         // rational = two long values, first is numerator, second is denominator:
32171         5: {
32172             getValue: function (dataView, dataOffset, littleEndian) {
32173                 return dataView.getUint32(dataOffset, littleEndian) /
32174                     dataView.getUint32(dataOffset + 4, littleEndian);
32175             },
32176             size: 8
32177         },
32178         // slong, 32 bit signed int:
32179         9: {
32180             getValue: function (dataView, dataOffset, littleEndian) {
32181                 return dataView.getInt32(dataOffset, littleEndian);
32182             },
32183             size: 4
32184         },
32185         // srational, two slongs, first is numerator, second is denominator:
32186         10: {
32187             getValue: function (dataView, dataOffset, littleEndian) {
32188                 return dataView.getInt32(dataOffset, littleEndian) /
32189                     dataView.getInt32(dataOffset + 4, littleEndian);
32190             },
32191             size: 8
32192         }
32193     },
32194     
32195     footer : {
32196         STANDARD : [
32197             {
32198                 tag : 'div',
32199                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32200                 action : 'rotate-left',
32201                 cn : [
32202                     {
32203                         tag : 'button',
32204                         cls : 'btn btn-default',
32205                         html : '<i class="fa fa-undo"></i>'
32206                     }
32207                 ]
32208             },
32209             {
32210                 tag : 'div',
32211                 cls : 'btn-group roo-upload-cropbox-picture',
32212                 action : 'picture',
32213                 cn : [
32214                     {
32215                         tag : 'button',
32216                         cls : 'btn btn-default',
32217                         html : '<i class="fa fa-picture-o"></i>'
32218                     }
32219                 ]
32220             },
32221             {
32222                 tag : 'div',
32223                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32224                 action : 'rotate-right',
32225                 cn : [
32226                     {
32227                         tag : 'button',
32228                         cls : 'btn btn-default',
32229                         html : '<i class="fa fa-repeat"></i>'
32230                     }
32231                 ]
32232             }
32233         ],
32234         DOCUMENT : [
32235             {
32236                 tag : 'div',
32237                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32238                 action : 'rotate-left',
32239                 cn : [
32240                     {
32241                         tag : 'button',
32242                         cls : 'btn btn-default',
32243                         html : '<i class="fa fa-undo"></i>'
32244                     }
32245                 ]
32246             },
32247             {
32248                 tag : 'div',
32249                 cls : 'btn-group roo-upload-cropbox-download',
32250                 action : 'download',
32251                 cn : [
32252                     {
32253                         tag : 'button',
32254                         cls : 'btn btn-default',
32255                         html : '<i class="fa fa-download"></i>'
32256                     }
32257                 ]
32258             },
32259             {
32260                 tag : 'div',
32261                 cls : 'btn-group roo-upload-cropbox-crop',
32262                 action : 'crop',
32263                 cn : [
32264                     {
32265                         tag : 'button',
32266                         cls : 'btn btn-default',
32267                         html : '<i class="fa fa-crop"></i>'
32268                     }
32269                 ]
32270             },
32271             {
32272                 tag : 'div',
32273                 cls : 'btn-group roo-upload-cropbox-trash',
32274                 action : 'trash',
32275                 cn : [
32276                     {
32277                         tag : 'button',
32278                         cls : 'btn btn-default',
32279                         html : '<i class="fa fa-trash"></i>'
32280                     }
32281                 ]
32282             },
32283             {
32284                 tag : 'div',
32285                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32286                 action : 'rotate-right',
32287                 cn : [
32288                     {
32289                         tag : 'button',
32290                         cls : 'btn btn-default',
32291                         html : '<i class="fa fa-repeat"></i>'
32292                     }
32293                 ]
32294             }
32295         ],
32296         ROTATOR : [
32297             {
32298                 tag : 'div',
32299                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32300                 action : 'rotate-left',
32301                 cn : [
32302                     {
32303                         tag : 'button',
32304                         cls : 'btn btn-default',
32305                         html : '<i class="fa fa-undo"></i>'
32306                     }
32307                 ]
32308             },
32309             {
32310                 tag : 'div',
32311                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32312                 action : 'rotate-right',
32313                 cn : [
32314                     {
32315                         tag : 'button',
32316                         cls : 'btn btn-default',
32317                         html : '<i class="fa fa-repeat"></i>'
32318                     }
32319                 ]
32320             }
32321         ]
32322     }
32323 });
32324
32325 /*
32326 * Licence: LGPL
32327 */
32328
32329 /**
32330  * @class Roo.bootstrap.DocumentManager
32331  * @extends Roo.bootstrap.Component
32332  * Bootstrap DocumentManager class
32333  * @cfg {String} paramName default 'imageUpload'
32334  * @cfg {String} toolTipName default 'filename'
32335  * @cfg {String} method default POST
32336  * @cfg {String} url action url
32337  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32338  * @cfg {Boolean} multiple multiple upload default true
32339  * @cfg {Number} thumbSize default 300
32340  * @cfg {String} fieldLabel
32341  * @cfg {Number} labelWidth default 4
32342  * @cfg {String} labelAlign (left|top) default left
32343  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32344 * @cfg {Number} labellg set the width of label (1-12)
32345  * @cfg {Number} labelmd set the width of label (1-12)
32346  * @cfg {Number} labelsm set the width of label (1-12)
32347  * @cfg {Number} labelxs set the width of label (1-12)
32348  * 
32349  * @constructor
32350  * Create a new DocumentManager
32351  * @param {Object} config The config object
32352  */
32353
32354 Roo.bootstrap.DocumentManager = function(config){
32355     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32356     
32357     this.files = [];
32358     this.delegates = [];
32359     
32360     this.addEvents({
32361         /**
32362          * @event initial
32363          * Fire when initial the DocumentManager
32364          * @param {Roo.bootstrap.DocumentManager} this
32365          */
32366         "initial" : true,
32367         /**
32368          * @event inspect
32369          * inspect selected file
32370          * @param {Roo.bootstrap.DocumentManager} this
32371          * @param {File} file
32372          */
32373         "inspect" : true,
32374         /**
32375          * @event exception
32376          * Fire when xhr load exception
32377          * @param {Roo.bootstrap.DocumentManager} this
32378          * @param {XMLHttpRequest} xhr
32379          */
32380         "exception" : true,
32381         /**
32382          * @event afterupload
32383          * Fire when xhr load exception
32384          * @param {Roo.bootstrap.DocumentManager} this
32385          * @param {XMLHttpRequest} xhr
32386          */
32387         "afterupload" : true,
32388         /**
32389          * @event prepare
32390          * prepare the form data
32391          * @param {Roo.bootstrap.DocumentManager} this
32392          * @param {Object} formData
32393          */
32394         "prepare" : true,
32395         /**
32396          * @event remove
32397          * Fire when remove the file
32398          * @param {Roo.bootstrap.DocumentManager} this
32399          * @param {Object} file
32400          */
32401         "remove" : true,
32402         /**
32403          * @event refresh
32404          * Fire after refresh the file
32405          * @param {Roo.bootstrap.DocumentManager} this
32406          */
32407         "refresh" : true,
32408         /**
32409          * @event click
32410          * Fire after click the image
32411          * @param {Roo.bootstrap.DocumentManager} this
32412          * @param {Object} file
32413          */
32414         "click" : true,
32415         /**
32416          * @event edit
32417          * Fire when upload a image and editable set to true
32418          * @param {Roo.bootstrap.DocumentManager} this
32419          * @param {Object} file
32420          */
32421         "edit" : true,
32422         /**
32423          * @event beforeselectfile
32424          * Fire before select file
32425          * @param {Roo.bootstrap.DocumentManager} this
32426          */
32427         "beforeselectfile" : true,
32428         /**
32429          * @event process
32430          * Fire before process file
32431          * @param {Roo.bootstrap.DocumentManager} this
32432          * @param {Object} file
32433          */
32434         "process" : true,
32435         /**
32436          * @event previewrendered
32437          * Fire when preview rendered
32438          * @param {Roo.bootstrap.DocumentManager} this
32439          * @param {Object} file
32440          */
32441         "previewrendered" : true,
32442         /**
32443          */
32444         "previewResize" : true
32445         
32446     });
32447 };
32448
32449 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32450     
32451     boxes : 0,
32452     inputName : '',
32453     thumbSize : 300,
32454     multiple : true,
32455     files : false,
32456     method : 'POST',
32457     url : '',
32458     paramName : 'imageUpload',
32459     toolTipName : 'filename',
32460     fieldLabel : '',
32461     labelWidth : 4,
32462     labelAlign : 'left',
32463     editable : true,
32464     delegates : false,
32465     xhr : false, 
32466     
32467     labellg : 0,
32468     labelmd : 0,
32469     labelsm : 0,
32470     labelxs : 0,
32471     
32472     getAutoCreate : function()
32473     {   
32474         var managerWidget = {
32475             tag : 'div',
32476             cls : 'roo-document-manager',
32477             cn : [
32478                 {
32479                     tag : 'input',
32480                     cls : 'roo-document-manager-selector',
32481                     type : 'file'
32482                 },
32483                 {
32484                     tag : 'div',
32485                     cls : 'roo-document-manager-uploader',
32486                     cn : [
32487                         {
32488                             tag : 'div',
32489                             cls : 'roo-document-manager-upload-btn',
32490                             html : '<i class="fa fa-plus"></i>'
32491                         }
32492                     ]
32493                     
32494                 }
32495             ]
32496         };
32497         
32498         var content = [
32499             {
32500                 tag : 'div',
32501                 cls : 'column col-md-12',
32502                 cn : managerWidget
32503             }
32504         ];
32505         
32506         if(this.fieldLabel.length){
32507             
32508             content = [
32509                 {
32510                     tag : 'div',
32511                     cls : 'column col-md-12',
32512                     html : this.fieldLabel
32513                 },
32514                 {
32515                     tag : 'div',
32516                     cls : 'column col-md-12',
32517                     cn : managerWidget
32518                 }
32519             ];
32520
32521             if(this.labelAlign == 'left'){
32522                 content = [
32523                     {
32524                         tag : 'div',
32525                         cls : 'column',
32526                         html : this.fieldLabel
32527                     },
32528                     {
32529                         tag : 'div',
32530                         cls : 'column',
32531                         cn : managerWidget
32532                     }
32533                 ];
32534                 
32535                 if(this.labelWidth > 12){
32536                     content[0].style = "width: " + this.labelWidth + 'px';
32537                 }
32538
32539                 if(this.labelWidth < 13 && this.labelmd == 0){
32540                     this.labelmd = this.labelWidth;
32541                 }
32542
32543                 if(this.labellg > 0){
32544                     content[0].cls += ' col-lg-' + this.labellg;
32545                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32546                 }
32547
32548                 if(this.labelmd > 0){
32549                     content[0].cls += ' col-md-' + this.labelmd;
32550                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32551                 }
32552
32553                 if(this.labelsm > 0){
32554                     content[0].cls += ' col-sm-' + this.labelsm;
32555                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32556                 }
32557
32558                 if(this.labelxs > 0){
32559                     content[0].cls += ' col-xs-' + this.labelxs;
32560                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32561                 }
32562                 
32563             }
32564         }
32565         
32566         var cfg = {
32567             tag : 'div',
32568             cls : 'row clearfix',
32569             cn : content
32570         };
32571         
32572         return cfg;
32573         
32574     },
32575     
32576     initEvents : function()
32577     {
32578         this.managerEl = this.el.select('.roo-document-manager', true).first();
32579         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32580         
32581         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32582         this.selectorEl.hide();
32583         
32584         if(this.multiple){
32585             this.selectorEl.attr('multiple', 'multiple');
32586         }
32587         
32588         this.selectorEl.on('change', this.onFileSelected, this);
32589         
32590         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32591         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32592         
32593         this.uploader.on('click', this.onUploaderClick, this);
32594         
32595         this.renderProgressDialog();
32596         
32597         var _this = this;
32598         
32599         window.addEventListener("resize", function() { _this.refresh(); } );
32600         
32601         this.fireEvent('initial', this);
32602     },
32603     
32604     renderProgressDialog : function()
32605     {
32606         var _this = this;
32607         
32608         this.progressDialog = new Roo.bootstrap.Modal({
32609             cls : 'roo-document-manager-progress-dialog',
32610             allow_close : false,
32611             animate : false,
32612             title : '',
32613             buttons : [
32614                 {
32615                     name  :'cancel',
32616                     weight : 'danger',
32617                     html : 'Cancel'
32618                 }
32619             ], 
32620             listeners : { 
32621                 btnclick : function() {
32622                     _this.uploadCancel();
32623                     this.hide();
32624                 }
32625             }
32626         });
32627          
32628         this.progressDialog.render(Roo.get(document.body));
32629          
32630         this.progress = new Roo.bootstrap.Progress({
32631             cls : 'roo-document-manager-progress',
32632             active : true,
32633             striped : true
32634         });
32635         
32636         this.progress.render(this.progressDialog.getChildContainer());
32637         
32638         this.progressBar = new Roo.bootstrap.ProgressBar({
32639             cls : 'roo-document-manager-progress-bar',
32640             aria_valuenow : 0,
32641             aria_valuemin : 0,
32642             aria_valuemax : 12,
32643             panel : 'success'
32644         });
32645         
32646         this.progressBar.render(this.progress.getChildContainer());
32647     },
32648     
32649     onUploaderClick : function(e)
32650     {
32651         e.preventDefault();
32652      
32653         if(this.fireEvent('beforeselectfile', this) != false){
32654             this.selectorEl.dom.click();
32655         }
32656         
32657     },
32658     
32659     onFileSelected : function(e)
32660     {
32661         e.preventDefault();
32662         
32663         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32664             return;
32665         }
32666         
32667         Roo.each(this.selectorEl.dom.files, function(file){
32668             if(this.fireEvent('inspect', this, file) != false){
32669                 this.files.push(file);
32670             }
32671         }, this);
32672         
32673         this.queue();
32674         
32675     },
32676     
32677     queue : function()
32678     {
32679         this.selectorEl.dom.value = '';
32680         
32681         if(!this.files || !this.files.length){
32682             return;
32683         }
32684         
32685         if(this.boxes > 0 && this.files.length > this.boxes){
32686             this.files = this.files.slice(0, this.boxes);
32687         }
32688         
32689         this.uploader.show();
32690         
32691         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32692             this.uploader.hide();
32693         }
32694         
32695         var _this = this;
32696         
32697         var files = [];
32698         
32699         var docs = [];
32700         
32701         Roo.each(this.files, function(file){
32702             
32703             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32704                 var f = this.renderPreview(file);
32705                 files.push(f);
32706                 return;
32707             }
32708             
32709             if(file.type.indexOf('image') != -1){
32710                 this.delegates.push(
32711                     (function(){
32712                         _this.process(file);
32713                     }).createDelegate(this)
32714                 );
32715         
32716                 return;
32717             }
32718             
32719             docs.push(
32720                 (function(){
32721                     _this.process(file);
32722                 }).createDelegate(this)
32723             );
32724             
32725         }, this);
32726         
32727         this.files = files;
32728         
32729         this.delegates = this.delegates.concat(docs);
32730         
32731         if(!this.delegates.length){
32732             this.refresh();
32733             return;
32734         }
32735         
32736         this.progressBar.aria_valuemax = this.delegates.length;
32737         
32738         this.arrange();
32739         
32740         return;
32741     },
32742     
32743     arrange : function()
32744     {
32745         if(!this.delegates.length){
32746             this.progressDialog.hide();
32747             this.refresh();
32748             return;
32749         }
32750         
32751         var delegate = this.delegates.shift();
32752         
32753         this.progressDialog.show();
32754         
32755         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32756         
32757         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32758         
32759         delegate();
32760     },
32761     
32762     refresh : function()
32763     {
32764         this.uploader.show();
32765         
32766         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32767             this.uploader.hide();
32768         }
32769         
32770         Roo.isTouch ? this.closable(false) : this.closable(true);
32771         
32772         this.fireEvent('refresh', this);
32773     },
32774     
32775     onRemove : function(e, el, o)
32776     {
32777         e.preventDefault();
32778         
32779         this.fireEvent('remove', this, o);
32780         
32781     },
32782     
32783     remove : function(o)
32784     {
32785         var files = [];
32786         
32787         Roo.each(this.files, function(file){
32788             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32789                 files.push(file);
32790                 return;
32791             }
32792
32793             o.target.remove();
32794
32795         }, this);
32796         
32797         this.files = files;
32798         
32799         this.refresh();
32800     },
32801     
32802     clear : function()
32803     {
32804         Roo.each(this.files, function(file){
32805             if(!file.target){
32806                 return;
32807             }
32808             
32809             file.target.remove();
32810
32811         }, this);
32812         
32813         this.files = [];
32814         
32815         this.refresh();
32816     },
32817     
32818     onClick : function(e, el, o)
32819     {
32820         e.preventDefault();
32821         
32822         this.fireEvent('click', this, o);
32823         
32824     },
32825     
32826     closable : function(closable)
32827     {
32828         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32829             
32830             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32831             
32832             if(closable){
32833                 el.show();
32834                 return;
32835             }
32836             
32837             el.hide();
32838             
32839         }, this);
32840     },
32841     
32842     xhrOnLoad : function(xhr)
32843     {
32844         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32845             el.remove();
32846         }, this);
32847         
32848         if (xhr.readyState !== 4) {
32849             this.arrange();
32850             this.fireEvent('exception', this, xhr);
32851             return;
32852         }
32853
32854         var response = Roo.decode(xhr.responseText);
32855         
32856         if(!response.success){
32857             this.arrange();
32858             this.fireEvent('exception', this, xhr);
32859             return;
32860         }
32861         
32862         var file = this.renderPreview(response.data);
32863         
32864         this.files.push(file);
32865         
32866         this.arrange();
32867         
32868         this.fireEvent('afterupload', this, xhr);
32869         
32870     },
32871     
32872     xhrOnError : function(xhr)
32873     {
32874         Roo.log('xhr on error');
32875         
32876         var response = Roo.decode(xhr.responseText);
32877           
32878         Roo.log(response);
32879         
32880         this.arrange();
32881     },
32882     
32883     process : function(file)
32884     {
32885         if(this.fireEvent('process', this, file) !== false){
32886             if(this.editable && file.type.indexOf('image') != -1){
32887                 this.fireEvent('edit', this, file);
32888                 return;
32889             }
32890
32891             this.uploadStart(file, false);
32892
32893             return;
32894         }
32895         
32896     },
32897     
32898     uploadStart : function(file, crop)
32899     {
32900         this.xhr = new XMLHttpRequest();
32901         
32902         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32903             this.arrange();
32904             return;
32905         }
32906         
32907         file.xhr = this.xhr;
32908             
32909         this.managerEl.createChild({
32910             tag : 'div',
32911             cls : 'roo-document-manager-loading',
32912             cn : [
32913                 {
32914                     tag : 'div',
32915                     tooltip : file.name,
32916                     cls : 'roo-document-manager-thumb',
32917                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32918                 }
32919             ]
32920
32921         });
32922
32923         this.xhr.open(this.method, this.url, true);
32924         
32925         var headers = {
32926             "Accept": "application/json",
32927             "Cache-Control": "no-cache",
32928             "X-Requested-With": "XMLHttpRequest"
32929         };
32930         
32931         for (var headerName in headers) {
32932             var headerValue = headers[headerName];
32933             if (headerValue) {
32934                 this.xhr.setRequestHeader(headerName, headerValue);
32935             }
32936         }
32937         
32938         var _this = this;
32939         
32940         this.xhr.onload = function()
32941         {
32942             _this.xhrOnLoad(_this.xhr);
32943         }
32944         
32945         this.xhr.onerror = function()
32946         {
32947             _this.xhrOnError(_this.xhr);
32948         }
32949         
32950         var formData = new FormData();
32951
32952         formData.append('returnHTML', 'NO');
32953         
32954         if(crop){
32955             formData.append('crop', crop);
32956         }
32957         
32958         formData.append(this.paramName, file, file.name);
32959         
32960         var options = {
32961             file : file, 
32962             manually : false
32963         };
32964         
32965         if(this.fireEvent('prepare', this, formData, options) != false){
32966             
32967             if(options.manually){
32968                 return;
32969             }
32970             
32971             this.xhr.send(formData);
32972             return;
32973         };
32974         
32975         this.uploadCancel();
32976     },
32977     
32978     uploadCancel : function()
32979     {
32980         if (this.xhr) {
32981             this.xhr.abort();
32982         }
32983         
32984         this.delegates = [];
32985         
32986         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32987             el.remove();
32988         }, this);
32989         
32990         this.arrange();
32991     },
32992     
32993     renderPreview : function(file)
32994     {
32995         if(typeof(file.target) != 'undefined' && file.target){
32996             return file;
32997         }
32998         
32999         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33000         
33001         var previewEl = this.managerEl.createChild({
33002             tag : 'div',
33003             cls : 'roo-document-manager-preview',
33004             cn : [
33005                 {
33006                     tag : 'div',
33007                     tooltip : file[this.toolTipName],
33008                     cls : 'roo-document-manager-thumb',
33009                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33010                 },
33011                 {
33012                     tag : 'button',
33013                     cls : 'close',
33014                     html : '<i class="fa fa-times-circle"></i>'
33015                 }
33016             ]
33017         });
33018
33019         var close = previewEl.select('button.close', true).first();
33020
33021         close.on('click', this.onRemove, this, file);
33022
33023         file.target = previewEl;
33024
33025         var image = previewEl.select('img', true).first();
33026         
33027         var _this = this;
33028         
33029         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33030         
33031         image.on('click', this.onClick, this, file);
33032         
33033         this.fireEvent('previewrendered', this, file);
33034         
33035         return file;
33036         
33037     },
33038     
33039     onPreviewLoad : function(file, image)
33040     {
33041         if(typeof(file.target) == 'undefined' || !file.target){
33042             return;
33043         }
33044         
33045         var width = image.dom.naturalWidth || image.dom.width;
33046         var height = image.dom.naturalHeight || image.dom.height;
33047         
33048         if(!this.previewResize) {
33049             return;
33050         }
33051         
33052         if(width > height){
33053             file.target.addClass('wide');
33054             return;
33055         }
33056         
33057         file.target.addClass('tall');
33058         return;
33059         
33060     },
33061     
33062     uploadFromSource : function(file, crop)
33063     {
33064         this.xhr = new XMLHttpRequest();
33065         
33066         this.managerEl.createChild({
33067             tag : 'div',
33068             cls : 'roo-document-manager-loading',
33069             cn : [
33070                 {
33071                     tag : 'div',
33072                     tooltip : file.name,
33073                     cls : 'roo-document-manager-thumb',
33074                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33075                 }
33076             ]
33077
33078         });
33079
33080         this.xhr.open(this.method, this.url, true);
33081         
33082         var headers = {
33083             "Accept": "application/json",
33084             "Cache-Control": "no-cache",
33085             "X-Requested-With": "XMLHttpRequest"
33086         };
33087         
33088         for (var headerName in headers) {
33089             var headerValue = headers[headerName];
33090             if (headerValue) {
33091                 this.xhr.setRequestHeader(headerName, headerValue);
33092             }
33093         }
33094         
33095         var _this = this;
33096         
33097         this.xhr.onload = function()
33098         {
33099             _this.xhrOnLoad(_this.xhr);
33100         }
33101         
33102         this.xhr.onerror = function()
33103         {
33104             _this.xhrOnError(_this.xhr);
33105         }
33106         
33107         var formData = new FormData();
33108
33109         formData.append('returnHTML', 'NO');
33110         
33111         formData.append('crop', crop);
33112         
33113         if(typeof(file.filename) != 'undefined'){
33114             formData.append('filename', file.filename);
33115         }
33116         
33117         if(typeof(file.mimetype) != 'undefined'){
33118             formData.append('mimetype', file.mimetype);
33119         }
33120         
33121         Roo.log(formData);
33122         
33123         if(this.fireEvent('prepare', this, formData) != false){
33124             this.xhr.send(formData);
33125         };
33126     }
33127 });
33128
33129 /*
33130 * Licence: LGPL
33131 */
33132
33133 /**
33134  * @class Roo.bootstrap.DocumentViewer
33135  * @extends Roo.bootstrap.Component
33136  * Bootstrap DocumentViewer class
33137  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33138  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33139  * 
33140  * @constructor
33141  * Create a new DocumentViewer
33142  * @param {Object} config The config object
33143  */
33144
33145 Roo.bootstrap.DocumentViewer = function(config){
33146     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33147     
33148     this.addEvents({
33149         /**
33150          * @event initial
33151          * Fire after initEvent
33152          * @param {Roo.bootstrap.DocumentViewer} this
33153          */
33154         "initial" : true,
33155         /**
33156          * @event click
33157          * Fire after click
33158          * @param {Roo.bootstrap.DocumentViewer} this
33159          */
33160         "click" : true,
33161         /**
33162          * @event download
33163          * Fire after download button
33164          * @param {Roo.bootstrap.DocumentViewer} this
33165          */
33166         "download" : true,
33167         /**
33168          * @event trash
33169          * Fire after trash button
33170          * @param {Roo.bootstrap.DocumentViewer} this
33171          */
33172         "trash" : true
33173         
33174     });
33175 };
33176
33177 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33178     
33179     showDownload : true,
33180     
33181     showTrash : true,
33182     
33183     getAutoCreate : function()
33184     {
33185         var cfg = {
33186             tag : 'div',
33187             cls : 'roo-document-viewer',
33188             cn : [
33189                 {
33190                     tag : 'div',
33191                     cls : 'roo-document-viewer-body',
33192                     cn : [
33193                         {
33194                             tag : 'div',
33195                             cls : 'roo-document-viewer-thumb',
33196                             cn : [
33197                                 {
33198                                     tag : 'img',
33199                                     cls : 'roo-document-viewer-image'
33200                                 }
33201                             ]
33202                         }
33203                     ]
33204                 },
33205                 {
33206                     tag : 'div',
33207                     cls : 'roo-document-viewer-footer',
33208                     cn : {
33209                         tag : 'div',
33210                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33211                         cn : [
33212                             {
33213                                 tag : 'div',
33214                                 cls : 'btn-group roo-document-viewer-download',
33215                                 cn : [
33216                                     {
33217                                         tag : 'button',
33218                                         cls : 'btn btn-default',
33219                                         html : '<i class="fa fa-download"></i>'
33220                                     }
33221                                 ]
33222                             },
33223                             {
33224                                 tag : 'div',
33225                                 cls : 'btn-group roo-document-viewer-trash',
33226                                 cn : [
33227                                     {
33228                                         tag : 'button',
33229                                         cls : 'btn btn-default',
33230                                         html : '<i class="fa fa-trash"></i>'
33231                                     }
33232                                 ]
33233                             }
33234                         ]
33235                     }
33236                 }
33237             ]
33238         };
33239         
33240         return cfg;
33241     },
33242     
33243     initEvents : function()
33244     {
33245         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33246         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33247         
33248         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33249         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33252         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33253         
33254         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33255         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33256         
33257         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33258         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33259         
33260         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33261         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33262         
33263         this.bodyEl.on('click', this.onClick, this);
33264         this.downloadBtn.on('click', this.onDownload, this);
33265         this.trashBtn.on('click', this.onTrash, this);
33266         
33267         this.downloadBtn.hide();
33268         this.trashBtn.hide();
33269         
33270         if(this.showDownload){
33271             this.downloadBtn.show();
33272         }
33273         
33274         if(this.showTrash){
33275             this.trashBtn.show();
33276         }
33277         
33278         if(!this.showDownload && !this.showTrash) {
33279             this.footerEl.hide();
33280         }
33281         
33282     },
33283     
33284     initial : function()
33285     {
33286         this.fireEvent('initial', this);
33287         
33288     },
33289     
33290     onClick : function(e)
33291     {
33292         e.preventDefault();
33293         
33294         this.fireEvent('click', this);
33295     },
33296     
33297     onDownload : function(e)
33298     {
33299         e.preventDefault();
33300         
33301         this.fireEvent('download', this);
33302     },
33303     
33304     onTrash : function(e)
33305     {
33306         e.preventDefault();
33307         
33308         this.fireEvent('trash', this);
33309     }
33310     
33311 });
33312 /*
33313  * - LGPL
33314  *
33315  * nav progress bar
33316  * 
33317  */
33318
33319 /**
33320  * @class Roo.bootstrap.NavProgressBar
33321  * @extends Roo.bootstrap.Component
33322  * Bootstrap NavProgressBar class
33323  * 
33324  * @constructor
33325  * Create a new nav progress bar
33326  * @param {Object} config The config object
33327  */
33328
33329 Roo.bootstrap.NavProgressBar = function(config){
33330     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33331
33332     this.bullets = this.bullets || [];
33333    
33334 //    Roo.bootstrap.NavProgressBar.register(this);
33335      this.addEvents({
33336         /**
33337              * @event changed
33338              * Fires when the active item changes
33339              * @param {Roo.bootstrap.NavProgressBar} this
33340              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33341              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33342          */
33343         'changed': true
33344      });
33345     
33346 };
33347
33348 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33349     
33350     bullets : [],
33351     barItems : [],
33352     
33353     getAutoCreate : function()
33354     {
33355         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33356         
33357         cfg = {
33358             tag : 'div',
33359             cls : 'roo-navigation-bar-group',
33360             cn : [
33361                 {
33362                     tag : 'div',
33363                     cls : 'roo-navigation-top-bar'
33364                 },
33365                 {
33366                     tag : 'div',
33367                     cls : 'roo-navigation-bullets-bar',
33368                     cn : [
33369                         {
33370                             tag : 'ul',
33371                             cls : 'roo-navigation-bar'
33372                         }
33373                     ]
33374                 },
33375                 
33376                 {
33377                     tag : 'div',
33378                     cls : 'roo-navigation-bottom-bar'
33379                 }
33380             ]
33381             
33382         };
33383         
33384         return cfg;
33385         
33386     },
33387     
33388     initEvents: function() 
33389     {
33390         
33391     },
33392     
33393     onRender : function(ct, position) 
33394     {
33395         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33396         
33397         if(this.bullets.length){
33398             Roo.each(this.bullets, function(b){
33399                this.addItem(b);
33400             }, this);
33401         }
33402         
33403         this.format();
33404         
33405     },
33406     
33407     addItem : function(cfg)
33408     {
33409         var item = new Roo.bootstrap.NavProgressItem(cfg);
33410         
33411         item.parentId = this.id;
33412         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33413         
33414         if(cfg.html){
33415             var top = new Roo.bootstrap.Element({
33416                 tag : 'div',
33417                 cls : 'roo-navigation-bar-text'
33418             });
33419             
33420             var bottom = new Roo.bootstrap.Element({
33421                 tag : 'div',
33422                 cls : 'roo-navigation-bar-text'
33423             });
33424             
33425             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33426             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33427             
33428             var topText = new Roo.bootstrap.Element({
33429                 tag : 'span',
33430                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33431             });
33432             
33433             var bottomText = new Roo.bootstrap.Element({
33434                 tag : 'span',
33435                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33436             });
33437             
33438             topText.onRender(top.el, null);
33439             bottomText.onRender(bottom.el, null);
33440             
33441             item.topEl = top;
33442             item.bottomEl = bottom;
33443         }
33444         
33445         this.barItems.push(item);
33446         
33447         return item;
33448     },
33449     
33450     getActive : function()
33451     {
33452         var active = false;
33453         
33454         Roo.each(this.barItems, function(v){
33455             
33456             if (!v.isActive()) {
33457                 return;
33458             }
33459             
33460             active = v;
33461             return false;
33462             
33463         });
33464         
33465         return active;
33466     },
33467     
33468     setActiveItem : function(item)
33469     {
33470         var prev = false;
33471         
33472         Roo.each(this.barItems, function(v){
33473             if (v.rid == item.rid) {
33474                 return ;
33475             }
33476             
33477             if (v.isActive()) {
33478                 v.setActive(false);
33479                 prev = v;
33480             }
33481         });
33482
33483         item.setActive(true);
33484         
33485         this.fireEvent('changed', this, item, prev);
33486     },
33487     
33488     getBarItem: function(rid)
33489     {
33490         var ret = false;
33491         
33492         Roo.each(this.barItems, function(e) {
33493             if (e.rid != rid) {
33494                 return;
33495             }
33496             
33497             ret =  e;
33498             return false;
33499         });
33500         
33501         return ret;
33502     },
33503     
33504     indexOfItem : function(item)
33505     {
33506         var index = false;
33507         
33508         Roo.each(this.barItems, function(v, i){
33509             
33510             if (v.rid != item.rid) {
33511                 return;
33512             }
33513             
33514             index = i;
33515             return false
33516         });
33517         
33518         return index;
33519     },
33520     
33521     setActiveNext : function()
33522     {
33523         var i = this.indexOfItem(this.getActive());
33524         
33525         if (i > this.barItems.length) {
33526             return;
33527         }
33528         
33529         this.setActiveItem(this.barItems[i+1]);
33530     },
33531     
33532     setActivePrev : function()
33533     {
33534         var i = this.indexOfItem(this.getActive());
33535         
33536         if (i  < 1) {
33537             return;
33538         }
33539         
33540         this.setActiveItem(this.barItems[i-1]);
33541     },
33542     
33543     format : function()
33544     {
33545         if(!this.barItems.length){
33546             return;
33547         }
33548      
33549         var width = 100 / this.barItems.length;
33550         
33551         Roo.each(this.barItems, function(i){
33552             i.el.setStyle('width', width + '%');
33553             i.topEl.el.setStyle('width', width + '%');
33554             i.bottomEl.el.setStyle('width', width + '%');
33555         }, this);
33556         
33557     }
33558     
33559 });
33560 /*
33561  * - LGPL
33562  *
33563  * Nav Progress Item
33564  * 
33565  */
33566
33567 /**
33568  * @class Roo.bootstrap.NavProgressItem
33569  * @extends Roo.bootstrap.Component
33570  * Bootstrap NavProgressItem class
33571  * @cfg {String} rid the reference id
33572  * @cfg {Boolean} active (true|false) Is item active default false
33573  * @cfg {Boolean} disabled (true|false) Is item active default false
33574  * @cfg {String} html
33575  * @cfg {String} position (top|bottom) text position default bottom
33576  * @cfg {String} icon show icon instead of number
33577  * 
33578  * @constructor
33579  * Create a new NavProgressItem
33580  * @param {Object} config The config object
33581  */
33582 Roo.bootstrap.NavProgressItem = function(config){
33583     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33584     this.addEvents({
33585         // raw events
33586         /**
33587          * @event click
33588          * The raw click event for the entire grid.
33589          * @param {Roo.bootstrap.NavProgressItem} this
33590          * @param {Roo.EventObject} e
33591          */
33592         "click" : true
33593     });
33594    
33595 };
33596
33597 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33598     
33599     rid : '',
33600     active : false,
33601     disabled : false,
33602     html : '',
33603     position : 'bottom',
33604     icon : false,
33605     
33606     getAutoCreate : function()
33607     {
33608         var iconCls = 'roo-navigation-bar-item-icon';
33609         
33610         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33611         
33612         var cfg = {
33613             tag: 'li',
33614             cls: 'roo-navigation-bar-item',
33615             cn : [
33616                 {
33617                     tag : 'i',
33618                     cls : iconCls
33619                 }
33620             ]
33621         };
33622         
33623         if(this.active){
33624             cfg.cls += ' active';
33625         }
33626         if(this.disabled){
33627             cfg.cls += ' disabled';
33628         }
33629         
33630         return cfg;
33631     },
33632     
33633     disable : function()
33634     {
33635         this.setDisabled(true);
33636     },
33637     
33638     enable : function()
33639     {
33640         this.setDisabled(false);
33641     },
33642     
33643     initEvents: function() 
33644     {
33645         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33646         
33647         this.iconEl.on('click', this.onClick, this);
33648     },
33649     
33650     onClick : function(e)
33651     {
33652         e.preventDefault();
33653         
33654         if(this.disabled){
33655             return;
33656         }
33657         
33658         if(this.fireEvent('click', this, e) === false){
33659             return;
33660         };
33661         
33662         this.parent().setActiveItem(this);
33663     },
33664     
33665     isActive: function () 
33666     {
33667         return this.active;
33668     },
33669     
33670     setActive : function(state)
33671     {
33672         if(this.active == state){
33673             return;
33674         }
33675         
33676         this.active = state;
33677         
33678         if (state) {
33679             this.el.addClass('active');
33680             return;
33681         }
33682         
33683         this.el.removeClass('active');
33684         
33685         return;
33686     },
33687     
33688     setDisabled : function(state)
33689     {
33690         if(this.disabled == state){
33691             return;
33692         }
33693         
33694         this.disabled = state;
33695         
33696         if (state) {
33697             this.el.addClass('disabled');
33698             return;
33699         }
33700         
33701         this.el.removeClass('disabled');
33702     },
33703     
33704     tooltipEl : function()
33705     {
33706         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33707     }
33708 });
33709  
33710
33711  /*
33712  * - LGPL
33713  *
33714  * FieldLabel
33715  * 
33716  */
33717
33718 /**
33719  * @class Roo.bootstrap.FieldLabel
33720  * @extends Roo.bootstrap.Component
33721  * Bootstrap FieldLabel class
33722  * @cfg {String} html contents of the element
33723  * @cfg {String} tag tag of the element default label
33724  * @cfg {String} cls class of the element
33725  * @cfg {String} target label target 
33726  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33727  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33728  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33729  * @cfg {String} iconTooltip default "This field is required"
33730  * @cfg {String} indicatorpos (left|right) default left
33731  * 
33732  * @constructor
33733  * Create a new FieldLabel
33734  * @param {Object} config The config object
33735  */
33736
33737 Roo.bootstrap.FieldLabel = function(config){
33738     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33739     
33740     this.addEvents({
33741             /**
33742              * @event invalid
33743              * Fires after the field has been marked as invalid.
33744              * @param {Roo.form.FieldLabel} this
33745              * @param {String} msg The validation message
33746              */
33747             invalid : true,
33748             /**
33749              * @event valid
33750              * Fires after the field has been validated with no errors.
33751              * @param {Roo.form.FieldLabel} this
33752              */
33753             valid : true
33754         });
33755 };
33756
33757 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33758     
33759     tag: 'label',
33760     cls: '',
33761     html: '',
33762     target: '',
33763     allowBlank : true,
33764     invalidClass : 'has-warning',
33765     validClass : 'has-success',
33766     iconTooltip : 'This field is required',
33767     indicatorpos : 'left',
33768     
33769     getAutoCreate : function(){
33770         
33771         var cls = "";
33772         if (!this.allowBlank) {
33773             cls  = "visible";
33774         }
33775         
33776         var cfg = {
33777             tag : this.tag,
33778             cls : 'roo-bootstrap-field-label ' + this.cls,
33779             for : this.target,
33780             cn : [
33781                 {
33782                     tag : 'i',
33783                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33784                     tooltip : this.iconTooltip
33785                 },
33786                 {
33787                     tag : 'span',
33788                     html : this.html
33789                 }
33790             ] 
33791         };
33792         
33793         if(this.indicatorpos == 'right'){
33794             var cfg = {
33795                 tag : this.tag,
33796                 cls : 'roo-bootstrap-field-label ' + this.cls,
33797                 for : this.target,
33798                 cn : [
33799                     {
33800                         tag : 'span',
33801                         html : this.html
33802                     },
33803                     {
33804                         tag : 'i',
33805                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33806                         tooltip : this.iconTooltip
33807                     }
33808                 ] 
33809             };
33810         }
33811         
33812         return cfg;
33813     },
33814     
33815     initEvents: function() 
33816     {
33817         Roo.bootstrap.Element.superclass.initEvents.call(this);
33818         
33819         this.indicator = this.indicatorEl();
33820         
33821         if(this.indicator){
33822             this.indicator.removeClass('visible');
33823             this.indicator.addClass('invisible');
33824         }
33825         
33826         Roo.bootstrap.FieldLabel.register(this);
33827     },
33828     
33829     indicatorEl : function()
33830     {
33831         var indicator = this.el.select('i.roo-required-indicator',true).first();
33832         
33833         if(!indicator){
33834             return false;
33835         }
33836         
33837         return indicator;
33838         
33839     },
33840     
33841     /**
33842      * Mark this field as valid
33843      */
33844     markValid : function()
33845     {
33846         if(this.indicator){
33847             this.indicator.removeClass('visible');
33848             this.indicator.addClass('invisible');
33849         }
33850         if (Roo.bootstrap.version == 3) {
33851             this.el.removeClass(this.invalidClass);
33852             this.el.addClass(this.validClass);
33853         } else {
33854             this.el.removeClass('is-invalid');
33855             this.el.addClass('is-valid');
33856         }
33857         
33858         
33859         this.fireEvent('valid', this);
33860     },
33861     
33862     /**
33863      * Mark this field as invalid
33864      * @param {String} msg The validation message
33865      */
33866     markInvalid : function(msg)
33867     {
33868         if(this.indicator){
33869             this.indicator.removeClass('invisible');
33870             this.indicator.addClass('visible');
33871         }
33872           if (Roo.bootstrap.version == 3) {
33873             this.el.removeClass(this.validClass);
33874             this.el.addClass(this.invalidClass);
33875         } else {
33876             this.el.removeClass('is-valid');
33877             this.el.addClass('is-invalid');
33878         }
33879         
33880         
33881         this.fireEvent('invalid', this, msg);
33882     }
33883     
33884    
33885 });
33886
33887 Roo.apply(Roo.bootstrap.FieldLabel, {
33888     
33889     groups: {},
33890     
33891      /**
33892     * register a FieldLabel Group
33893     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33894     */
33895     register : function(label)
33896     {
33897         if(this.groups.hasOwnProperty(label.target)){
33898             return;
33899         }
33900      
33901         this.groups[label.target] = label;
33902         
33903     },
33904     /**
33905     * fetch a FieldLabel Group based on the target
33906     * @param {string} target
33907     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33908     */
33909     get: function(target) {
33910         if (typeof(this.groups[target]) == 'undefined') {
33911             return false;
33912         }
33913         
33914         return this.groups[target] ;
33915     }
33916 });
33917
33918  
33919
33920  /*
33921  * - LGPL
33922  *
33923  * page DateSplitField.
33924  * 
33925  */
33926
33927
33928 /**
33929  * @class Roo.bootstrap.DateSplitField
33930  * @extends Roo.bootstrap.Component
33931  * Bootstrap DateSplitField class
33932  * @cfg {string} fieldLabel - the label associated
33933  * @cfg {Number} labelWidth set the width of label (0-12)
33934  * @cfg {String} labelAlign (top|left)
33935  * @cfg {Boolean} dayAllowBlank (true|false) default false
33936  * @cfg {Boolean} monthAllowBlank (true|false) default false
33937  * @cfg {Boolean} yearAllowBlank (true|false) default false
33938  * @cfg {string} dayPlaceholder 
33939  * @cfg {string} monthPlaceholder
33940  * @cfg {string} yearPlaceholder
33941  * @cfg {string} dayFormat default 'd'
33942  * @cfg {string} monthFormat default 'm'
33943  * @cfg {string} yearFormat default 'Y'
33944  * @cfg {Number} labellg set the width of label (1-12)
33945  * @cfg {Number} labelmd set the width of label (1-12)
33946  * @cfg {Number} labelsm set the width of label (1-12)
33947  * @cfg {Number} labelxs set the width of label (1-12)
33948
33949  *     
33950  * @constructor
33951  * Create a new DateSplitField
33952  * @param {Object} config The config object
33953  */
33954
33955 Roo.bootstrap.DateSplitField = function(config){
33956     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33957     
33958     this.addEvents({
33959         // raw events
33960          /**
33961          * @event years
33962          * getting the data of years
33963          * @param {Roo.bootstrap.DateSplitField} this
33964          * @param {Object} years
33965          */
33966         "years" : true,
33967         /**
33968          * @event days
33969          * getting the data of days
33970          * @param {Roo.bootstrap.DateSplitField} this
33971          * @param {Object} days
33972          */
33973         "days" : true,
33974         /**
33975          * @event invalid
33976          * Fires after the field has been marked as invalid.
33977          * @param {Roo.form.Field} this
33978          * @param {String} msg The validation message
33979          */
33980         invalid : true,
33981        /**
33982          * @event valid
33983          * Fires after the field has been validated with no errors.
33984          * @param {Roo.form.Field} this
33985          */
33986         valid : true
33987     });
33988 };
33989
33990 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33991     
33992     fieldLabel : '',
33993     labelAlign : 'top',
33994     labelWidth : 3,
33995     dayAllowBlank : false,
33996     monthAllowBlank : false,
33997     yearAllowBlank : false,
33998     dayPlaceholder : '',
33999     monthPlaceholder : '',
34000     yearPlaceholder : '',
34001     dayFormat : 'd',
34002     monthFormat : 'm',
34003     yearFormat : 'Y',
34004     isFormField : true,
34005     labellg : 0,
34006     labelmd : 0,
34007     labelsm : 0,
34008     labelxs : 0,
34009     
34010     getAutoCreate : function()
34011     {
34012         var cfg = {
34013             tag : 'div',
34014             cls : 'row roo-date-split-field-group',
34015             cn : [
34016                 {
34017                     tag : 'input',
34018                     type : 'hidden',
34019                     cls : 'form-hidden-field roo-date-split-field-group-value',
34020                     name : this.name
34021                 }
34022             ]
34023         };
34024         
34025         var labelCls = 'col-md-12';
34026         var contentCls = 'col-md-4';
34027         
34028         if(this.fieldLabel){
34029             
34030             var label = {
34031                 tag : 'div',
34032                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34033                 cn : [
34034                     {
34035                         tag : 'label',
34036                         html : this.fieldLabel
34037                     }
34038                 ]
34039             };
34040             
34041             if(this.labelAlign == 'left'){
34042             
34043                 if(this.labelWidth > 12){
34044                     label.style = "width: " + this.labelWidth + 'px';
34045                 }
34046
34047                 if(this.labelWidth < 13 && this.labelmd == 0){
34048                     this.labelmd = this.labelWidth;
34049                 }
34050
34051                 if(this.labellg > 0){
34052                     labelCls = ' col-lg-' + this.labellg;
34053                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34054                 }
34055
34056                 if(this.labelmd > 0){
34057                     labelCls = ' col-md-' + this.labelmd;
34058                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34059                 }
34060
34061                 if(this.labelsm > 0){
34062                     labelCls = ' col-sm-' + this.labelsm;
34063                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34064                 }
34065
34066                 if(this.labelxs > 0){
34067                     labelCls = ' col-xs-' + this.labelxs;
34068                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34069                 }
34070             }
34071             
34072             label.cls += ' ' + labelCls;
34073             
34074             cfg.cn.push(label);
34075         }
34076         
34077         Roo.each(['day', 'month', 'year'], function(t){
34078             cfg.cn.push({
34079                 tag : 'div',
34080                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34081             });
34082         }, this);
34083         
34084         return cfg;
34085     },
34086     
34087     inputEl: function ()
34088     {
34089         return this.el.select('.roo-date-split-field-group-value', true).first();
34090     },
34091     
34092     onRender : function(ct, position) 
34093     {
34094         var _this = this;
34095         
34096         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34097         
34098         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34099         
34100         this.dayField = new Roo.bootstrap.ComboBox({
34101             allowBlank : this.dayAllowBlank,
34102             alwaysQuery : true,
34103             displayField : 'value',
34104             editable : false,
34105             fieldLabel : '',
34106             forceSelection : true,
34107             mode : 'local',
34108             placeholder : this.dayPlaceholder,
34109             selectOnFocus : true,
34110             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34111             triggerAction : 'all',
34112             typeAhead : true,
34113             valueField : 'value',
34114             store : new Roo.data.SimpleStore({
34115                 data : (function() {    
34116                     var days = [];
34117                     _this.fireEvent('days', _this, days);
34118                     return days;
34119                 })(),
34120                 fields : [ 'value' ]
34121             }),
34122             listeners : {
34123                 select : function (_self, record, index)
34124                 {
34125                     _this.setValue(_this.getValue());
34126                 }
34127             }
34128         });
34129
34130         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34131         
34132         this.monthField = new Roo.bootstrap.MonthField({
34133             after : '<i class=\"fa fa-calendar\"></i>',
34134             allowBlank : this.monthAllowBlank,
34135             placeholder : this.monthPlaceholder,
34136             readOnly : true,
34137             listeners : {
34138                 render : function (_self)
34139                 {
34140                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34141                         e.preventDefault();
34142                         _self.focus();
34143                     });
34144                 },
34145                 select : function (_self, oldvalue, newvalue)
34146                 {
34147                     _this.setValue(_this.getValue());
34148                 }
34149             }
34150         });
34151         
34152         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34153         
34154         this.yearField = new Roo.bootstrap.ComboBox({
34155             allowBlank : this.yearAllowBlank,
34156             alwaysQuery : true,
34157             displayField : 'value',
34158             editable : false,
34159             fieldLabel : '',
34160             forceSelection : true,
34161             mode : 'local',
34162             placeholder : this.yearPlaceholder,
34163             selectOnFocus : true,
34164             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34165             triggerAction : 'all',
34166             typeAhead : true,
34167             valueField : 'value',
34168             store : new Roo.data.SimpleStore({
34169                 data : (function() {
34170                     var years = [];
34171                     _this.fireEvent('years', _this, years);
34172                     return years;
34173                 })(),
34174                 fields : [ 'value' ]
34175             }),
34176             listeners : {
34177                 select : function (_self, record, index)
34178                 {
34179                     _this.setValue(_this.getValue());
34180                 }
34181             }
34182         });
34183
34184         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34185     },
34186     
34187     setValue : function(v, format)
34188     {
34189         this.inputEl.dom.value = v;
34190         
34191         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34192         
34193         var d = Date.parseDate(v, f);
34194         
34195         if(!d){
34196             this.validate();
34197             return;
34198         }
34199         
34200         this.setDay(d.format(this.dayFormat));
34201         this.setMonth(d.format(this.monthFormat));
34202         this.setYear(d.format(this.yearFormat));
34203         
34204         this.validate();
34205         
34206         return;
34207     },
34208     
34209     setDay : function(v)
34210     {
34211         this.dayField.setValue(v);
34212         this.inputEl.dom.value = this.getValue();
34213         this.validate();
34214         return;
34215     },
34216     
34217     setMonth : function(v)
34218     {
34219         this.monthField.setValue(v, true);
34220         this.inputEl.dom.value = this.getValue();
34221         this.validate();
34222         return;
34223     },
34224     
34225     setYear : function(v)
34226     {
34227         this.yearField.setValue(v);
34228         this.inputEl.dom.value = this.getValue();
34229         this.validate();
34230         return;
34231     },
34232     
34233     getDay : function()
34234     {
34235         return this.dayField.getValue();
34236     },
34237     
34238     getMonth : function()
34239     {
34240         return this.monthField.getValue();
34241     },
34242     
34243     getYear : function()
34244     {
34245         return this.yearField.getValue();
34246     },
34247     
34248     getValue : function()
34249     {
34250         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34251         
34252         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34253         
34254         return date;
34255     },
34256     
34257     reset : function()
34258     {
34259         this.setDay('');
34260         this.setMonth('');
34261         this.setYear('');
34262         this.inputEl.dom.value = '';
34263         this.validate();
34264         return;
34265     },
34266     
34267     validate : function()
34268     {
34269         var d = this.dayField.validate();
34270         var m = this.monthField.validate();
34271         var y = this.yearField.validate();
34272         
34273         var valid = true;
34274         
34275         if(
34276                 (!this.dayAllowBlank && !d) ||
34277                 (!this.monthAllowBlank && !m) ||
34278                 (!this.yearAllowBlank && !y)
34279         ){
34280             valid = false;
34281         }
34282         
34283         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34284             return valid;
34285         }
34286         
34287         if(valid){
34288             this.markValid();
34289             return valid;
34290         }
34291         
34292         this.markInvalid();
34293         
34294         return valid;
34295     },
34296     
34297     markValid : function()
34298     {
34299         
34300         var label = this.el.select('label', true).first();
34301         var icon = this.el.select('i.fa-star', true).first();
34302
34303         if(label && icon){
34304             icon.remove();
34305         }
34306         
34307         this.fireEvent('valid', this);
34308     },
34309     
34310      /**
34311      * Mark this field as invalid
34312      * @param {String} msg The validation message
34313      */
34314     markInvalid : function(msg)
34315     {
34316         
34317         var label = this.el.select('label', true).first();
34318         var icon = this.el.select('i.fa-star', true).first();
34319
34320         if(label && !icon){
34321             this.el.select('.roo-date-split-field-label', true).createChild({
34322                 tag : 'i',
34323                 cls : 'text-danger fa fa-lg fa-star',
34324                 tooltip : 'This field is required',
34325                 style : 'margin-right:5px;'
34326             }, label, true);
34327         }
34328         
34329         this.fireEvent('invalid', this, msg);
34330     },
34331     
34332     clearInvalid : function()
34333     {
34334         var label = this.el.select('label', true).first();
34335         var icon = this.el.select('i.fa-star', true).first();
34336
34337         if(label && icon){
34338             icon.remove();
34339         }
34340         
34341         this.fireEvent('valid', this);
34342     },
34343     
34344     getName: function()
34345     {
34346         return this.name;
34347     }
34348     
34349 });
34350
34351  /**
34352  *
34353  * This is based on 
34354  * http://masonry.desandro.com
34355  *
34356  * The idea is to render all the bricks based on vertical width...
34357  *
34358  * The original code extends 'outlayer' - we might need to use that....
34359  * 
34360  */
34361
34362
34363 /**
34364  * @class Roo.bootstrap.LayoutMasonry
34365  * @extends Roo.bootstrap.Component
34366  * Bootstrap Layout Masonry class
34367  * 
34368  * @constructor
34369  * Create a new Element
34370  * @param {Object} config The config object
34371  */
34372
34373 Roo.bootstrap.LayoutMasonry = function(config){
34374     
34375     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34376     
34377     this.bricks = [];
34378     
34379     Roo.bootstrap.LayoutMasonry.register(this);
34380     
34381     this.addEvents({
34382         // raw events
34383         /**
34384          * @event layout
34385          * Fire after layout the items
34386          * @param {Roo.bootstrap.LayoutMasonry} this
34387          * @param {Roo.EventObject} e
34388          */
34389         "layout" : true
34390     });
34391     
34392 };
34393
34394 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34395     
34396     /**
34397      * @cfg {Boolean} isLayoutInstant = no animation?
34398      */   
34399     isLayoutInstant : false, // needed?
34400    
34401     /**
34402      * @cfg {Number} boxWidth  width of the columns
34403      */   
34404     boxWidth : 450,
34405     
34406       /**
34407      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34408      */   
34409     boxHeight : 0,
34410     
34411     /**
34412      * @cfg {Number} padWidth padding below box..
34413      */   
34414     padWidth : 10, 
34415     
34416     /**
34417      * @cfg {Number} gutter gutter width..
34418      */   
34419     gutter : 10,
34420     
34421      /**
34422      * @cfg {Number} maxCols maximum number of columns
34423      */   
34424     
34425     maxCols: 0,
34426     
34427     /**
34428      * @cfg {Boolean} isAutoInitial defalut true
34429      */   
34430     isAutoInitial : true, 
34431     
34432     containerWidth: 0,
34433     
34434     /**
34435      * @cfg {Boolean} isHorizontal defalut false
34436      */   
34437     isHorizontal : false, 
34438
34439     currentSize : null,
34440     
34441     tag: 'div',
34442     
34443     cls: '',
34444     
34445     bricks: null, //CompositeElement
34446     
34447     cols : 1,
34448     
34449     _isLayoutInited : false,
34450     
34451 //    isAlternative : false, // only use for vertical layout...
34452     
34453     /**
34454      * @cfg {Number} alternativePadWidth padding below box..
34455      */   
34456     alternativePadWidth : 50,
34457     
34458     selectedBrick : [],
34459     
34460     getAutoCreate : function(){
34461         
34462         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34463         
34464         var cfg = {
34465             tag: this.tag,
34466             cls: 'blog-masonary-wrapper ' + this.cls,
34467             cn : {
34468                 cls : 'mas-boxes masonary'
34469             }
34470         };
34471         
34472         return cfg;
34473     },
34474     
34475     getChildContainer: function( )
34476     {
34477         if (this.boxesEl) {
34478             return this.boxesEl;
34479         }
34480         
34481         this.boxesEl = this.el.select('.mas-boxes').first();
34482         
34483         return this.boxesEl;
34484     },
34485     
34486     
34487     initEvents : function()
34488     {
34489         var _this = this;
34490         
34491         if(this.isAutoInitial){
34492             Roo.log('hook children rendered');
34493             this.on('childrenrendered', function() {
34494                 Roo.log('children rendered');
34495                 _this.initial();
34496             } ,this);
34497         }
34498     },
34499     
34500     initial : function()
34501     {
34502         this.selectedBrick = [];
34503         
34504         this.currentSize = this.el.getBox(true);
34505         
34506         Roo.EventManager.onWindowResize(this.resize, this); 
34507
34508         if(!this.isAutoInitial){
34509             this.layout();
34510             return;
34511         }
34512         
34513         this.layout();
34514         
34515         return;
34516         //this.layout.defer(500,this);
34517         
34518     },
34519     
34520     resize : function()
34521     {
34522         var cs = this.el.getBox(true);
34523         
34524         if (
34525                 this.currentSize.width == cs.width && 
34526                 this.currentSize.x == cs.x && 
34527                 this.currentSize.height == cs.height && 
34528                 this.currentSize.y == cs.y 
34529         ) {
34530             Roo.log("no change in with or X or Y");
34531             return;
34532         }
34533         
34534         this.currentSize = cs;
34535         
34536         this.layout();
34537         
34538     },
34539     
34540     layout : function()
34541     {   
34542         this._resetLayout();
34543         
34544         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34545         
34546         this.layoutItems( isInstant );
34547       
34548         this._isLayoutInited = true;
34549         
34550         this.fireEvent('layout', this);
34551         
34552     },
34553     
34554     _resetLayout : function()
34555     {
34556         if(this.isHorizontal){
34557             this.horizontalMeasureColumns();
34558             return;
34559         }
34560         
34561         this.verticalMeasureColumns();
34562         
34563     },
34564     
34565     verticalMeasureColumns : function()
34566     {
34567         this.getContainerWidth();
34568         
34569 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34570 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34571 //            return;
34572 //        }
34573         
34574         var boxWidth = this.boxWidth + this.padWidth;
34575         
34576         if(this.containerWidth < this.boxWidth){
34577             boxWidth = this.containerWidth
34578         }
34579         
34580         var containerWidth = this.containerWidth;
34581         
34582         var cols = Math.floor(containerWidth / boxWidth);
34583         
34584         this.cols = Math.max( cols, 1 );
34585         
34586         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34587         
34588         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34589         
34590         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34591         
34592         this.colWidth = boxWidth + avail - this.padWidth;
34593         
34594         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34595         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34596     },
34597     
34598     horizontalMeasureColumns : function()
34599     {
34600         this.getContainerWidth();
34601         
34602         var boxWidth = this.boxWidth;
34603         
34604         if(this.containerWidth < boxWidth){
34605             boxWidth = this.containerWidth;
34606         }
34607         
34608         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34609         
34610         this.el.setHeight(boxWidth);
34611         
34612     },
34613     
34614     getContainerWidth : function()
34615     {
34616         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34617     },
34618     
34619     layoutItems : function( isInstant )
34620     {
34621         Roo.log(this.bricks);
34622         
34623         var items = Roo.apply([], this.bricks);
34624         
34625         if(this.isHorizontal){
34626             this._horizontalLayoutItems( items , isInstant );
34627             return;
34628         }
34629         
34630 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34631 //            this._verticalAlternativeLayoutItems( items , isInstant );
34632 //            return;
34633 //        }
34634         
34635         this._verticalLayoutItems( items , isInstant );
34636         
34637     },
34638     
34639     _verticalLayoutItems : function ( items , isInstant)
34640     {
34641         if ( !items || !items.length ) {
34642             return;
34643         }
34644         
34645         var standard = [
34646             ['xs', 'xs', 'xs', 'tall'],
34647             ['xs', 'xs', 'tall'],
34648             ['xs', 'xs', 'sm'],
34649             ['xs', 'xs', 'xs'],
34650             ['xs', 'tall'],
34651             ['xs', 'sm'],
34652             ['xs', 'xs'],
34653             ['xs'],
34654             
34655             ['sm', 'xs', 'xs'],
34656             ['sm', 'xs'],
34657             ['sm'],
34658             
34659             ['tall', 'xs', 'xs', 'xs'],
34660             ['tall', 'xs', 'xs'],
34661             ['tall', 'xs'],
34662             ['tall']
34663             
34664         ];
34665         
34666         var queue = [];
34667         
34668         var boxes = [];
34669         
34670         var box = [];
34671         
34672         Roo.each(items, function(item, k){
34673             
34674             switch (item.size) {
34675                 // these layouts take up a full box,
34676                 case 'md' :
34677                 case 'md-left' :
34678                 case 'md-right' :
34679                 case 'wide' :
34680                     
34681                     if(box.length){
34682                         boxes.push(box);
34683                         box = [];
34684                     }
34685                     
34686                     boxes.push([item]);
34687                     
34688                     break;
34689                     
34690                 case 'xs' :
34691                 case 'sm' :
34692                 case 'tall' :
34693                     
34694                     box.push(item);
34695                     
34696                     break;
34697                 default :
34698                     break;
34699                     
34700             }
34701             
34702         }, this);
34703         
34704         if(box.length){
34705             boxes.push(box);
34706             box = [];
34707         }
34708         
34709         var filterPattern = function(box, length)
34710         {
34711             if(!box.length){
34712                 return;
34713             }
34714             
34715             var match = false;
34716             
34717             var pattern = box.slice(0, length);
34718             
34719             var format = [];
34720             
34721             Roo.each(pattern, function(i){
34722                 format.push(i.size);
34723             }, this);
34724             
34725             Roo.each(standard, function(s){
34726                 
34727                 if(String(s) != String(format)){
34728                     return;
34729                 }
34730                 
34731                 match = true;
34732                 return false;
34733                 
34734             }, this);
34735             
34736             if(!match && length == 1){
34737                 return;
34738             }
34739             
34740             if(!match){
34741                 filterPattern(box, length - 1);
34742                 return;
34743             }
34744                 
34745             queue.push(pattern);
34746
34747             box = box.slice(length, box.length);
34748
34749             filterPattern(box, 4);
34750
34751             return;
34752             
34753         }
34754         
34755         Roo.each(boxes, function(box, k){
34756             
34757             if(!box.length){
34758                 return;
34759             }
34760             
34761             if(box.length == 1){
34762                 queue.push(box);
34763                 return;
34764             }
34765             
34766             filterPattern(box, 4);
34767             
34768         }, this);
34769         
34770         this._processVerticalLayoutQueue( queue, isInstant );
34771         
34772     },
34773     
34774 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34775 //    {
34776 //        if ( !items || !items.length ) {
34777 //            return;
34778 //        }
34779 //
34780 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34781 //        
34782 //    },
34783     
34784     _horizontalLayoutItems : function ( items , isInstant)
34785     {
34786         if ( !items || !items.length || items.length < 3) {
34787             return;
34788         }
34789         
34790         items.reverse();
34791         
34792         var eItems = items.slice(0, 3);
34793         
34794         items = items.slice(3, items.length);
34795         
34796         var standard = [
34797             ['xs', 'xs', 'xs', 'wide'],
34798             ['xs', 'xs', 'wide'],
34799             ['xs', 'xs', 'sm'],
34800             ['xs', 'xs', 'xs'],
34801             ['xs', 'wide'],
34802             ['xs', 'sm'],
34803             ['xs', 'xs'],
34804             ['xs'],
34805             
34806             ['sm', 'xs', 'xs'],
34807             ['sm', 'xs'],
34808             ['sm'],
34809             
34810             ['wide', 'xs', 'xs', 'xs'],
34811             ['wide', 'xs', 'xs'],
34812             ['wide', 'xs'],
34813             ['wide'],
34814             
34815             ['wide-thin']
34816         ];
34817         
34818         var queue = [];
34819         
34820         var boxes = [];
34821         
34822         var box = [];
34823         
34824         Roo.each(items, function(item, k){
34825             
34826             switch (item.size) {
34827                 case 'md' :
34828                 case 'md-left' :
34829                 case 'md-right' :
34830                 case 'tall' :
34831                     
34832                     if(box.length){
34833                         boxes.push(box);
34834                         box = [];
34835                     }
34836                     
34837                     boxes.push([item]);
34838                     
34839                     break;
34840                     
34841                 case 'xs' :
34842                 case 'sm' :
34843                 case 'wide' :
34844                 case 'wide-thin' :
34845                     
34846                     box.push(item);
34847                     
34848                     break;
34849                 default :
34850                     break;
34851                     
34852             }
34853             
34854         }, this);
34855         
34856         if(box.length){
34857             boxes.push(box);
34858             box = [];
34859         }
34860         
34861         var filterPattern = function(box, length)
34862         {
34863             if(!box.length){
34864                 return;
34865             }
34866             
34867             var match = false;
34868             
34869             var pattern = box.slice(0, length);
34870             
34871             var format = [];
34872             
34873             Roo.each(pattern, function(i){
34874                 format.push(i.size);
34875             }, this);
34876             
34877             Roo.each(standard, function(s){
34878                 
34879                 if(String(s) != String(format)){
34880                     return;
34881                 }
34882                 
34883                 match = true;
34884                 return false;
34885                 
34886             }, this);
34887             
34888             if(!match && length == 1){
34889                 return;
34890             }
34891             
34892             if(!match){
34893                 filterPattern(box, length - 1);
34894                 return;
34895             }
34896                 
34897             queue.push(pattern);
34898
34899             box = box.slice(length, box.length);
34900
34901             filterPattern(box, 4);
34902
34903             return;
34904             
34905         }
34906         
34907         Roo.each(boxes, function(box, k){
34908             
34909             if(!box.length){
34910                 return;
34911             }
34912             
34913             if(box.length == 1){
34914                 queue.push(box);
34915                 return;
34916             }
34917             
34918             filterPattern(box, 4);
34919             
34920         }, this);
34921         
34922         
34923         var prune = [];
34924         
34925         var pos = this.el.getBox(true);
34926         
34927         var minX = pos.x;
34928         
34929         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34930         
34931         var hit_end = false;
34932         
34933         Roo.each(queue, function(box){
34934             
34935             if(hit_end){
34936                 
34937                 Roo.each(box, function(b){
34938                 
34939                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34940                     b.el.hide();
34941
34942                 }, this);
34943
34944                 return;
34945             }
34946             
34947             var mx = 0;
34948             
34949             Roo.each(box, function(b){
34950                 
34951                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34952                 b.el.show();
34953
34954                 mx = Math.max(mx, b.x);
34955                 
34956             }, this);
34957             
34958             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34959             
34960             if(maxX < minX){
34961                 
34962                 Roo.each(box, function(b){
34963                 
34964                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34965                     b.el.hide();
34966                     
34967                 }, this);
34968                 
34969                 hit_end = true;
34970                 
34971                 return;
34972             }
34973             
34974             prune.push(box);
34975             
34976         }, this);
34977         
34978         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34979     },
34980     
34981     /** Sets position of item in DOM
34982     * @param {Element} item
34983     * @param {Number} x - horizontal position
34984     * @param {Number} y - vertical position
34985     * @param {Boolean} isInstant - disables transitions
34986     */
34987     _processVerticalLayoutQueue : function( queue, isInstant )
34988     {
34989         var pos = this.el.getBox(true);
34990         var x = pos.x;
34991         var y = pos.y;
34992         var maxY = [];
34993         
34994         for (var i = 0; i < this.cols; i++){
34995             maxY[i] = pos.y;
34996         }
34997         
34998         Roo.each(queue, function(box, k){
34999             
35000             var col = k % this.cols;
35001             
35002             Roo.each(box, function(b,kk){
35003                 
35004                 b.el.position('absolute');
35005                 
35006                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35007                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35008                 
35009                 if(b.size == 'md-left' || b.size == 'md-right'){
35010                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35011                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35012                 }
35013                 
35014                 b.el.setWidth(width);
35015                 b.el.setHeight(height);
35016                 // iframe?
35017                 b.el.select('iframe',true).setSize(width,height);
35018                 
35019             }, this);
35020             
35021             for (var i = 0; i < this.cols; i++){
35022                 
35023                 if(maxY[i] < maxY[col]){
35024                     col = i;
35025                     continue;
35026                 }
35027                 
35028                 col = Math.min(col, i);
35029                 
35030             }
35031             
35032             x = pos.x + col * (this.colWidth + this.padWidth);
35033             
35034             y = maxY[col];
35035             
35036             var positions = [];
35037             
35038             switch (box.length){
35039                 case 1 :
35040                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35041                     break;
35042                 case 2 :
35043                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35044                     break;
35045                 case 3 :
35046                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35047                     break;
35048                 case 4 :
35049                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35050                     break;
35051                 default :
35052                     break;
35053             }
35054             
35055             Roo.each(box, function(b,kk){
35056                 
35057                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35058                 
35059                 var sz = b.el.getSize();
35060                 
35061                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35062                 
35063             }, this);
35064             
35065         }, this);
35066         
35067         var mY = 0;
35068         
35069         for (var i = 0; i < this.cols; i++){
35070             mY = Math.max(mY, maxY[i]);
35071         }
35072         
35073         this.el.setHeight(mY - pos.y);
35074         
35075     },
35076     
35077 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35078 //    {
35079 //        var pos = this.el.getBox(true);
35080 //        var x = pos.x;
35081 //        var y = pos.y;
35082 //        var maxX = pos.right;
35083 //        
35084 //        var maxHeight = 0;
35085 //        
35086 //        Roo.each(items, function(item, k){
35087 //            
35088 //            var c = k % 2;
35089 //            
35090 //            item.el.position('absolute');
35091 //                
35092 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35093 //
35094 //            item.el.setWidth(width);
35095 //
35096 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35097 //
35098 //            item.el.setHeight(height);
35099 //            
35100 //            if(c == 0){
35101 //                item.el.setXY([x, y], isInstant ? false : true);
35102 //            } else {
35103 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35104 //            }
35105 //            
35106 //            y = y + height + this.alternativePadWidth;
35107 //            
35108 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35109 //            
35110 //        }, this);
35111 //        
35112 //        this.el.setHeight(maxHeight);
35113 //        
35114 //    },
35115     
35116     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35117     {
35118         var pos = this.el.getBox(true);
35119         
35120         var minX = pos.x;
35121         var minY = pos.y;
35122         
35123         var maxX = pos.right;
35124         
35125         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35126         
35127         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35128         
35129         Roo.each(queue, function(box, k){
35130             
35131             Roo.each(box, function(b, kk){
35132                 
35133                 b.el.position('absolute');
35134                 
35135                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35136                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35137                 
35138                 if(b.size == 'md-left' || b.size == 'md-right'){
35139                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35140                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35141                 }
35142                 
35143                 b.el.setWidth(width);
35144                 b.el.setHeight(height);
35145                 
35146             }, this);
35147             
35148             if(!box.length){
35149                 return;
35150             }
35151             
35152             var positions = [];
35153             
35154             switch (box.length){
35155                 case 1 :
35156                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35157                     break;
35158                 case 2 :
35159                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35160                     break;
35161                 case 3 :
35162                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35163                     break;
35164                 case 4 :
35165                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35166                     break;
35167                 default :
35168                     break;
35169             }
35170             
35171             Roo.each(box, function(b,kk){
35172                 
35173                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35174                 
35175                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35176                 
35177             }, this);
35178             
35179         }, this);
35180         
35181     },
35182     
35183     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35184     {
35185         Roo.each(eItems, function(b,k){
35186             
35187             b.size = (k == 0) ? 'sm' : 'xs';
35188             b.x = (k == 0) ? 2 : 1;
35189             b.y = (k == 0) ? 2 : 1;
35190             
35191             b.el.position('absolute');
35192             
35193             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35194                 
35195             b.el.setWidth(width);
35196             
35197             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35198             
35199             b.el.setHeight(height);
35200             
35201         }, this);
35202
35203         var positions = [];
35204         
35205         positions.push({
35206             x : maxX - this.unitWidth * 2 - this.gutter,
35207             y : minY
35208         });
35209         
35210         positions.push({
35211             x : maxX - this.unitWidth,
35212             y : minY + (this.unitWidth + this.gutter) * 2
35213         });
35214         
35215         positions.push({
35216             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35217             y : minY
35218         });
35219         
35220         Roo.each(eItems, function(b,k){
35221             
35222             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35223
35224         }, this);
35225         
35226     },
35227     
35228     getVerticalOneBoxColPositions : function(x, y, box)
35229     {
35230         var pos = [];
35231         
35232         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35233         
35234         if(box[0].size == 'md-left'){
35235             rand = 0;
35236         }
35237         
35238         if(box[0].size == 'md-right'){
35239             rand = 1;
35240         }
35241         
35242         pos.push({
35243             x : x + (this.unitWidth + this.gutter) * rand,
35244             y : y
35245         });
35246         
35247         return pos;
35248     },
35249     
35250     getVerticalTwoBoxColPositions : function(x, y, box)
35251     {
35252         var pos = [];
35253         
35254         if(box[0].size == 'xs'){
35255             
35256             pos.push({
35257                 x : x,
35258                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35259             });
35260
35261             pos.push({
35262                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35263                 y : y
35264             });
35265             
35266             return pos;
35267             
35268         }
35269         
35270         pos.push({
35271             x : x,
35272             y : y
35273         });
35274
35275         pos.push({
35276             x : x + (this.unitWidth + this.gutter) * 2,
35277             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35278         });
35279         
35280         return pos;
35281         
35282     },
35283     
35284     getVerticalThreeBoxColPositions : function(x, y, box)
35285     {
35286         var pos = [];
35287         
35288         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35289             
35290             pos.push({
35291                 x : x,
35292                 y : y
35293             });
35294
35295             pos.push({
35296                 x : x + (this.unitWidth + this.gutter) * 1,
35297                 y : y
35298             });
35299             
35300             pos.push({
35301                 x : x + (this.unitWidth + this.gutter) * 2,
35302                 y : y
35303             });
35304             
35305             return pos;
35306             
35307         }
35308         
35309         if(box[0].size == 'xs' && box[1].size == 'xs'){
35310             
35311             pos.push({
35312                 x : x,
35313                 y : y
35314             });
35315
35316             pos.push({
35317                 x : x,
35318                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35319             });
35320             
35321             pos.push({
35322                 x : x + (this.unitWidth + this.gutter) * 1,
35323                 y : y
35324             });
35325             
35326             return pos;
35327             
35328         }
35329         
35330         pos.push({
35331             x : x,
35332             y : y
35333         });
35334
35335         pos.push({
35336             x : x + (this.unitWidth + this.gutter) * 2,
35337             y : y
35338         });
35339
35340         pos.push({
35341             x : x + (this.unitWidth + this.gutter) * 2,
35342             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35343         });
35344             
35345         return pos;
35346         
35347     },
35348     
35349     getVerticalFourBoxColPositions : function(x, y, box)
35350     {
35351         var pos = [];
35352         
35353         if(box[0].size == 'xs'){
35354             
35355             pos.push({
35356                 x : x,
35357                 y : y
35358             });
35359
35360             pos.push({
35361                 x : x,
35362                 y : y + (this.unitHeight + this.gutter) * 1
35363             });
35364             
35365             pos.push({
35366                 x : x,
35367                 y : y + (this.unitHeight + this.gutter) * 2
35368             });
35369             
35370             pos.push({
35371                 x : x + (this.unitWidth + this.gutter) * 1,
35372                 y : y
35373             });
35374             
35375             return pos;
35376             
35377         }
35378         
35379         pos.push({
35380             x : x,
35381             y : y
35382         });
35383
35384         pos.push({
35385             x : x + (this.unitWidth + this.gutter) * 2,
35386             y : y
35387         });
35388
35389         pos.push({
35390             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35391             y : y + (this.unitHeight + this.gutter) * 1
35392         });
35393
35394         pos.push({
35395             x : x + (this.unitWidth + this.gutter) * 2,
35396             y : y + (this.unitWidth + this.gutter) * 2
35397         });
35398
35399         return pos;
35400         
35401     },
35402     
35403     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35404     {
35405         var pos = [];
35406         
35407         if(box[0].size == 'md-left'){
35408             pos.push({
35409                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35410                 y : minY
35411             });
35412             
35413             return pos;
35414         }
35415         
35416         if(box[0].size == 'md-right'){
35417             pos.push({
35418                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35419                 y : minY + (this.unitWidth + this.gutter) * 1
35420             });
35421             
35422             return pos;
35423         }
35424         
35425         var rand = Math.floor(Math.random() * (4 - box[0].y));
35426         
35427         pos.push({
35428             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35429             y : minY + (this.unitWidth + this.gutter) * rand
35430         });
35431         
35432         return pos;
35433         
35434     },
35435     
35436     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35437     {
35438         var pos = [];
35439         
35440         if(box[0].size == 'xs'){
35441             
35442             pos.push({
35443                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35444                 y : minY
35445             });
35446
35447             pos.push({
35448                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35449                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35450             });
35451             
35452             return pos;
35453             
35454         }
35455         
35456         pos.push({
35457             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35458             y : minY
35459         });
35460
35461         pos.push({
35462             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35463             y : minY + (this.unitWidth + this.gutter) * 2
35464         });
35465         
35466         return pos;
35467         
35468     },
35469     
35470     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35471     {
35472         var pos = [];
35473         
35474         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35475             
35476             pos.push({
35477                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35478                 y : minY
35479             });
35480
35481             pos.push({
35482                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35483                 y : minY + (this.unitWidth + this.gutter) * 1
35484             });
35485             
35486             pos.push({
35487                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35488                 y : minY + (this.unitWidth + this.gutter) * 2
35489             });
35490             
35491             return pos;
35492             
35493         }
35494         
35495         if(box[0].size == 'xs' && box[1].size == 'xs'){
35496             
35497             pos.push({
35498                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35499                 y : minY
35500             });
35501
35502             pos.push({
35503                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35504                 y : minY
35505             });
35506             
35507             pos.push({
35508                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35509                 y : minY + (this.unitWidth + this.gutter) * 1
35510             });
35511             
35512             return pos;
35513             
35514         }
35515         
35516         pos.push({
35517             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35518             y : minY
35519         });
35520
35521         pos.push({
35522             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35523             y : minY + (this.unitWidth + this.gutter) * 2
35524         });
35525
35526         pos.push({
35527             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35528             y : minY + (this.unitWidth + this.gutter) * 2
35529         });
35530             
35531         return pos;
35532         
35533     },
35534     
35535     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35536     {
35537         var pos = [];
35538         
35539         if(box[0].size == 'xs'){
35540             
35541             pos.push({
35542                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35543                 y : minY
35544             });
35545
35546             pos.push({
35547                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35548                 y : minY
35549             });
35550             
35551             pos.push({
35552                 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),
35553                 y : minY
35554             });
35555             
35556             pos.push({
35557                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35558                 y : minY + (this.unitWidth + this.gutter) * 1
35559             });
35560             
35561             return pos;
35562             
35563         }
35564         
35565         pos.push({
35566             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35567             y : minY
35568         });
35569         
35570         pos.push({
35571             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35572             y : minY + (this.unitWidth + this.gutter) * 2
35573         });
35574         
35575         pos.push({
35576             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35577             y : minY + (this.unitWidth + this.gutter) * 2
35578         });
35579         
35580         pos.push({
35581             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),
35582             y : minY + (this.unitWidth + this.gutter) * 2
35583         });
35584
35585         return pos;
35586         
35587     },
35588     
35589     /**
35590     * remove a Masonry Brick
35591     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35592     */
35593     removeBrick : function(brick_id)
35594     {
35595         if (!brick_id) {
35596             return;
35597         }
35598         
35599         for (var i = 0; i<this.bricks.length; i++) {
35600             if (this.bricks[i].id == brick_id) {
35601                 this.bricks.splice(i,1);
35602                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35603                 this.initial();
35604             }
35605         }
35606     },
35607     
35608     /**
35609     * adds a Masonry Brick
35610     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35611     */
35612     addBrick : function(cfg)
35613     {
35614         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35615         //this.register(cn);
35616         cn.parentId = this.id;
35617         cn.render(this.el);
35618         return cn;
35619     },
35620     
35621     /**
35622     * register a Masonry Brick
35623     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35624     */
35625     
35626     register : function(brick)
35627     {
35628         this.bricks.push(brick);
35629         brick.masonryId = this.id;
35630     },
35631     
35632     /**
35633     * clear all the Masonry Brick
35634     */
35635     clearAll : function()
35636     {
35637         this.bricks = [];
35638         //this.getChildContainer().dom.innerHTML = "";
35639         this.el.dom.innerHTML = '';
35640     },
35641     
35642     getSelected : function()
35643     {
35644         if (!this.selectedBrick) {
35645             return false;
35646         }
35647         
35648         return this.selectedBrick;
35649     }
35650 });
35651
35652 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35653     
35654     groups: {},
35655      /**
35656     * register a Masonry Layout
35657     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35658     */
35659     
35660     register : function(layout)
35661     {
35662         this.groups[layout.id] = layout;
35663     },
35664     /**
35665     * fetch a  Masonry Layout based on the masonry layout ID
35666     * @param {string} the masonry layout to add
35667     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35668     */
35669     
35670     get: function(layout_id) {
35671         if (typeof(this.groups[layout_id]) == 'undefined') {
35672             return false;
35673         }
35674         return this.groups[layout_id] ;
35675     }
35676     
35677     
35678     
35679 });
35680
35681  
35682
35683  /**
35684  *
35685  * This is based on 
35686  * http://masonry.desandro.com
35687  *
35688  * The idea is to render all the bricks based on vertical width...
35689  *
35690  * The original code extends 'outlayer' - we might need to use that....
35691  * 
35692  */
35693
35694
35695 /**
35696  * @class Roo.bootstrap.LayoutMasonryAuto
35697  * @extends Roo.bootstrap.Component
35698  * Bootstrap Layout Masonry class
35699  * 
35700  * @constructor
35701  * Create a new Element
35702  * @param {Object} config The config object
35703  */
35704
35705 Roo.bootstrap.LayoutMasonryAuto = function(config){
35706     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35707 };
35708
35709 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35710     
35711       /**
35712      * @cfg {Boolean} isFitWidth  - resize the width..
35713      */   
35714     isFitWidth : false,  // options..
35715     /**
35716      * @cfg {Boolean} isOriginLeft = left align?
35717      */   
35718     isOriginLeft : true,
35719     /**
35720      * @cfg {Boolean} isOriginTop = top align?
35721      */   
35722     isOriginTop : false,
35723     /**
35724      * @cfg {Boolean} isLayoutInstant = no animation?
35725      */   
35726     isLayoutInstant : false, // needed?
35727     /**
35728      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35729      */   
35730     isResizingContainer : true,
35731     /**
35732      * @cfg {Number} columnWidth  width of the columns 
35733      */   
35734     
35735     columnWidth : 0,
35736     
35737     /**
35738      * @cfg {Number} maxCols maximum number of columns
35739      */   
35740     
35741     maxCols: 0,
35742     /**
35743      * @cfg {Number} padHeight padding below box..
35744      */   
35745     
35746     padHeight : 10, 
35747     
35748     /**
35749      * @cfg {Boolean} isAutoInitial defalut true
35750      */   
35751     
35752     isAutoInitial : true, 
35753     
35754     // private?
35755     gutter : 0,
35756     
35757     containerWidth: 0,
35758     initialColumnWidth : 0,
35759     currentSize : null,
35760     
35761     colYs : null, // array.
35762     maxY : 0,
35763     padWidth: 10,
35764     
35765     
35766     tag: 'div',
35767     cls: '',
35768     bricks: null, //CompositeElement
35769     cols : 0, // array?
35770     // element : null, // wrapped now this.el
35771     _isLayoutInited : null, 
35772     
35773     
35774     getAutoCreate : function(){
35775         
35776         var cfg = {
35777             tag: this.tag,
35778             cls: 'blog-masonary-wrapper ' + this.cls,
35779             cn : {
35780                 cls : 'mas-boxes masonary'
35781             }
35782         };
35783         
35784         return cfg;
35785     },
35786     
35787     getChildContainer: function( )
35788     {
35789         if (this.boxesEl) {
35790             return this.boxesEl;
35791         }
35792         
35793         this.boxesEl = this.el.select('.mas-boxes').first();
35794         
35795         return this.boxesEl;
35796     },
35797     
35798     
35799     initEvents : function()
35800     {
35801         var _this = this;
35802         
35803         if(this.isAutoInitial){
35804             Roo.log('hook children rendered');
35805             this.on('childrenrendered', function() {
35806                 Roo.log('children rendered');
35807                 _this.initial();
35808             } ,this);
35809         }
35810         
35811     },
35812     
35813     initial : function()
35814     {
35815         this.reloadItems();
35816
35817         this.currentSize = this.el.getBox(true);
35818
35819         /// was window resize... - let's see if this works..
35820         Roo.EventManager.onWindowResize(this.resize, this); 
35821
35822         if(!this.isAutoInitial){
35823             this.layout();
35824             return;
35825         }
35826         
35827         this.layout.defer(500,this);
35828     },
35829     
35830     reloadItems: function()
35831     {
35832         this.bricks = this.el.select('.masonry-brick', true);
35833         
35834         this.bricks.each(function(b) {
35835             //Roo.log(b.getSize());
35836             if (!b.attr('originalwidth')) {
35837                 b.attr('originalwidth',  b.getSize().width);
35838             }
35839             
35840         });
35841         
35842         Roo.log(this.bricks.elements.length);
35843     },
35844     
35845     resize : function()
35846     {
35847         Roo.log('resize');
35848         var cs = this.el.getBox(true);
35849         
35850         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35851             Roo.log("no change in with or X");
35852             return;
35853         }
35854         this.currentSize = cs;
35855         this.layout();
35856     },
35857     
35858     layout : function()
35859     {
35860          Roo.log('layout');
35861         this._resetLayout();
35862         //this._manageStamps();
35863       
35864         // don't animate first layout
35865         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35866         this.layoutItems( isInstant );
35867       
35868         // flag for initalized
35869         this._isLayoutInited = true;
35870     },
35871     
35872     layoutItems : function( isInstant )
35873     {
35874         //var items = this._getItemsForLayout( this.items );
35875         // original code supports filtering layout items.. we just ignore it..
35876         
35877         this._layoutItems( this.bricks , isInstant );
35878       
35879         this._postLayout();
35880     },
35881     _layoutItems : function ( items , isInstant)
35882     {
35883        //this.fireEvent( 'layout', this, items );
35884     
35885
35886         if ( !items || !items.elements.length ) {
35887           // no items, emit event with empty array
35888             return;
35889         }
35890
35891         var queue = [];
35892         items.each(function(item) {
35893             Roo.log("layout item");
35894             Roo.log(item);
35895             // get x/y object from method
35896             var position = this._getItemLayoutPosition( item );
35897             // enqueue
35898             position.item = item;
35899             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35900             queue.push( position );
35901         }, this);
35902       
35903         this._processLayoutQueue( queue );
35904     },
35905     /** Sets position of item in DOM
35906     * @param {Element} item
35907     * @param {Number} x - horizontal position
35908     * @param {Number} y - vertical position
35909     * @param {Boolean} isInstant - disables transitions
35910     */
35911     _processLayoutQueue : function( queue )
35912     {
35913         for ( var i=0, len = queue.length; i < len; i++ ) {
35914             var obj = queue[i];
35915             obj.item.position('absolute');
35916             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35917         }
35918     },
35919       
35920     
35921     /**
35922     * Any logic you want to do after each layout,
35923     * i.e. size the container
35924     */
35925     _postLayout : function()
35926     {
35927         this.resizeContainer();
35928     },
35929     
35930     resizeContainer : function()
35931     {
35932         if ( !this.isResizingContainer ) {
35933             return;
35934         }
35935         var size = this._getContainerSize();
35936         if ( size ) {
35937             this.el.setSize(size.width,size.height);
35938             this.boxesEl.setSize(size.width,size.height);
35939         }
35940     },
35941     
35942     
35943     
35944     _resetLayout : function()
35945     {
35946         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35947         this.colWidth = this.el.getWidth();
35948         //this.gutter = this.el.getWidth(); 
35949         
35950         this.measureColumns();
35951
35952         // reset column Y
35953         var i = this.cols;
35954         this.colYs = [];
35955         while (i--) {
35956             this.colYs.push( 0 );
35957         }
35958     
35959         this.maxY = 0;
35960     },
35961
35962     measureColumns : function()
35963     {
35964         this.getContainerWidth();
35965       // if columnWidth is 0, default to outerWidth of first item
35966         if ( !this.columnWidth ) {
35967             var firstItem = this.bricks.first();
35968             Roo.log(firstItem);
35969             this.columnWidth  = this.containerWidth;
35970             if (firstItem && firstItem.attr('originalwidth') ) {
35971                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35972             }
35973             // columnWidth fall back to item of first element
35974             Roo.log("set column width?");
35975                         this.initialColumnWidth = this.columnWidth  ;
35976
35977             // if first elem has no width, default to size of container
35978             
35979         }
35980         
35981         
35982         if (this.initialColumnWidth) {
35983             this.columnWidth = this.initialColumnWidth;
35984         }
35985         
35986         
35987             
35988         // column width is fixed at the top - however if container width get's smaller we should
35989         // reduce it...
35990         
35991         // this bit calcs how man columns..
35992             
35993         var columnWidth = this.columnWidth += this.gutter;
35994       
35995         // calculate columns
35996         var containerWidth = this.containerWidth + this.gutter;
35997         
35998         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35999         // fix rounding errors, typically with gutters
36000         var excess = columnWidth - containerWidth % columnWidth;
36001         
36002         
36003         // if overshoot is less than a pixel, round up, otherwise floor it
36004         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36005         cols = Math[ mathMethod ]( cols );
36006         this.cols = Math.max( cols, 1 );
36007         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36008         
36009          // padding positioning..
36010         var totalColWidth = this.cols * this.columnWidth;
36011         var padavail = this.containerWidth - totalColWidth;
36012         // so for 2 columns - we need 3 'pads'
36013         
36014         var padNeeded = (1+this.cols) * this.padWidth;
36015         
36016         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36017         
36018         this.columnWidth += padExtra
36019         //this.padWidth = Math.floor(padavail /  ( this.cols));
36020         
36021         // adjust colum width so that padding is fixed??
36022         
36023         // we have 3 columns ... total = width * 3
36024         // we have X left over... that should be used by 
36025         
36026         //if (this.expandC) {
36027             
36028         //}
36029         
36030         
36031         
36032     },
36033     
36034     getContainerWidth : function()
36035     {
36036        /* // container is parent if fit width
36037         var container = this.isFitWidth ? this.element.parentNode : this.element;
36038         // check that this.size and size are there
36039         // IE8 triggers resize on body size change, so they might not be
36040         
36041         var size = getSize( container );  //FIXME
36042         this.containerWidth = size && size.innerWidth; //FIXME
36043         */
36044          
36045         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36046         
36047     },
36048     
36049     _getItemLayoutPosition : function( item )  // what is item?
36050     {
36051         // we resize the item to our columnWidth..
36052       
36053         item.setWidth(this.columnWidth);
36054         item.autoBoxAdjust  = false;
36055         
36056         var sz = item.getSize();
36057  
36058         // how many columns does this brick span
36059         var remainder = this.containerWidth % this.columnWidth;
36060         
36061         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36062         // round if off by 1 pixel, otherwise use ceil
36063         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36064         colSpan = Math.min( colSpan, this.cols );
36065         
36066         // normally this should be '1' as we dont' currently allow multi width columns..
36067         
36068         var colGroup = this._getColGroup( colSpan );
36069         // get the minimum Y value from the columns
36070         var minimumY = Math.min.apply( Math, colGroup );
36071         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36072         
36073         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36074          
36075         // position the brick
36076         var position = {
36077             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36078             y: this.currentSize.y + minimumY + this.padHeight
36079         };
36080         
36081         Roo.log(position);
36082         // apply setHeight to necessary columns
36083         var setHeight = minimumY + sz.height + this.padHeight;
36084         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36085         
36086         var setSpan = this.cols + 1 - colGroup.length;
36087         for ( var i = 0; i < setSpan; i++ ) {
36088           this.colYs[ shortColIndex + i ] = setHeight ;
36089         }
36090       
36091         return position;
36092     },
36093     
36094     /**
36095      * @param {Number} colSpan - number of columns the element spans
36096      * @returns {Array} colGroup
36097      */
36098     _getColGroup : function( colSpan )
36099     {
36100         if ( colSpan < 2 ) {
36101           // if brick spans only one column, use all the column Ys
36102           return this.colYs;
36103         }
36104       
36105         var colGroup = [];
36106         // how many different places could this brick fit horizontally
36107         var groupCount = this.cols + 1 - colSpan;
36108         // for each group potential horizontal position
36109         for ( var i = 0; i < groupCount; i++ ) {
36110           // make an array of colY values for that one group
36111           var groupColYs = this.colYs.slice( i, i + colSpan );
36112           // and get the max value of the array
36113           colGroup[i] = Math.max.apply( Math, groupColYs );
36114         }
36115         return colGroup;
36116     },
36117     /*
36118     _manageStamp : function( stamp )
36119     {
36120         var stampSize =  stamp.getSize();
36121         var offset = stamp.getBox();
36122         // get the columns that this stamp affects
36123         var firstX = this.isOriginLeft ? offset.x : offset.right;
36124         var lastX = firstX + stampSize.width;
36125         var firstCol = Math.floor( firstX / this.columnWidth );
36126         firstCol = Math.max( 0, firstCol );
36127         
36128         var lastCol = Math.floor( lastX / this.columnWidth );
36129         // lastCol should not go over if multiple of columnWidth #425
36130         lastCol -= lastX % this.columnWidth ? 0 : 1;
36131         lastCol = Math.min( this.cols - 1, lastCol );
36132         
36133         // set colYs to bottom of the stamp
36134         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36135             stampSize.height;
36136             
36137         for ( var i = firstCol; i <= lastCol; i++ ) {
36138           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36139         }
36140     },
36141     */
36142     
36143     _getContainerSize : function()
36144     {
36145         this.maxY = Math.max.apply( Math, this.colYs );
36146         var size = {
36147             height: this.maxY
36148         };
36149       
36150         if ( this.isFitWidth ) {
36151             size.width = this._getContainerFitWidth();
36152         }
36153       
36154         return size;
36155     },
36156     
36157     _getContainerFitWidth : function()
36158     {
36159         var unusedCols = 0;
36160         // count unused columns
36161         var i = this.cols;
36162         while ( --i ) {
36163           if ( this.colYs[i] !== 0 ) {
36164             break;
36165           }
36166           unusedCols++;
36167         }
36168         // fit container to columns that have been used
36169         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36170     },
36171     
36172     needsResizeLayout : function()
36173     {
36174         var previousWidth = this.containerWidth;
36175         this.getContainerWidth();
36176         return previousWidth !== this.containerWidth;
36177     }
36178  
36179 });
36180
36181  
36182
36183  /*
36184  * - LGPL
36185  *
36186  * element
36187  * 
36188  */
36189
36190 /**
36191  * @class Roo.bootstrap.MasonryBrick
36192  * @extends Roo.bootstrap.Component
36193  * Bootstrap MasonryBrick class
36194  * 
36195  * @constructor
36196  * Create a new MasonryBrick
36197  * @param {Object} config The config object
36198  */
36199
36200 Roo.bootstrap.MasonryBrick = function(config){
36201     
36202     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36203     
36204     Roo.bootstrap.MasonryBrick.register(this);
36205     
36206     this.addEvents({
36207         // raw events
36208         /**
36209          * @event click
36210          * When a MasonryBrick is clcik
36211          * @param {Roo.bootstrap.MasonryBrick} this
36212          * @param {Roo.EventObject} e
36213          */
36214         "click" : true
36215     });
36216 };
36217
36218 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36219     
36220     /**
36221      * @cfg {String} title
36222      */   
36223     title : '',
36224     /**
36225      * @cfg {String} html
36226      */   
36227     html : '',
36228     /**
36229      * @cfg {String} bgimage
36230      */   
36231     bgimage : '',
36232     /**
36233      * @cfg {String} videourl
36234      */   
36235     videourl : '',
36236     /**
36237      * @cfg {String} cls
36238      */   
36239     cls : '',
36240     /**
36241      * @cfg {String} href
36242      */   
36243     href : '',
36244     /**
36245      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36246      */   
36247     size : 'xs',
36248     
36249     /**
36250      * @cfg {String} placetitle (center|bottom)
36251      */   
36252     placetitle : '',
36253     
36254     /**
36255      * @cfg {Boolean} isFitContainer defalut true
36256      */   
36257     isFitContainer : true, 
36258     
36259     /**
36260      * @cfg {Boolean} preventDefault defalut false
36261      */   
36262     preventDefault : false, 
36263     
36264     /**
36265      * @cfg {Boolean} inverse defalut false
36266      */   
36267     maskInverse : false, 
36268     
36269     getAutoCreate : function()
36270     {
36271         if(!this.isFitContainer){
36272             return this.getSplitAutoCreate();
36273         }
36274         
36275         var cls = 'masonry-brick masonry-brick-full';
36276         
36277         if(this.href.length){
36278             cls += ' masonry-brick-link';
36279         }
36280         
36281         if(this.bgimage.length){
36282             cls += ' masonry-brick-image';
36283         }
36284         
36285         if(this.maskInverse){
36286             cls += ' mask-inverse';
36287         }
36288         
36289         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36290             cls += ' enable-mask';
36291         }
36292         
36293         if(this.size){
36294             cls += ' masonry-' + this.size + '-brick';
36295         }
36296         
36297         if(this.placetitle.length){
36298             
36299             switch (this.placetitle) {
36300                 case 'center' :
36301                     cls += ' masonry-center-title';
36302                     break;
36303                 case 'bottom' :
36304                     cls += ' masonry-bottom-title';
36305                     break;
36306                 default:
36307                     break;
36308             }
36309             
36310         } else {
36311             if(!this.html.length && !this.bgimage.length){
36312                 cls += ' masonry-center-title';
36313             }
36314
36315             if(!this.html.length && this.bgimage.length){
36316                 cls += ' masonry-bottom-title';
36317             }
36318         }
36319         
36320         if(this.cls){
36321             cls += ' ' + this.cls;
36322         }
36323         
36324         var cfg = {
36325             tag: (this.href.length) ? 'a' : 'div',
36326             cls: cls,
36327             cn: [
36328                 {
36329                     tag: 'div',
36330                     cls: 'masonry-brick-mask'
36331                 },
36332                 {
36333                     tag: 'div',
36334                     cls: 'masonry-brick-paragraph',
36335                     cn: []
36336                 }
36337             ]
36338         };
36339         
36340         if(this.href.length){
36341             cfg.href = this.href;
36342         }
36343         
36344         var cn = cfg.cn[1].cn;
36345         
36346         if(this.title.length){
36347             cn.push({
36348                 tag: 'h4',
36349                 cls: 'masonry-brick-title',
36350                 html: this.title
36351             });
36352         }
36353         
36354         if(this.html.length){
36355             cn.push({
36356                 tag: 'p',
36357                 cls: 'masonry-brick-text',
36358                 html: this.html
36359             });
36360         }
36361         
36362         if (!this.title.length && !this.html.length) {
36363             cfg.cn[1].cls += ' hide';
36364         }
36365         
36366         if(this.bgimage.length){
36367             cfg.cn.push({
36368                 tag: 'img',
36369                 cls: 'masonry-brick-image-view',
36370                 src: this.bgimage
36371             });
36372         }
36373         
36374         if(this.videourl.length){
36375             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36376             // youtube support only?
36377             cfg.cn.push({
36378                 tag: 'iframe',
36379                 cls: 'masonry-brick-image-view',
36380                 src: vurl,
36381                 frameborder : 0,
36382                 allowfullscreen : true
36383             });
36384         }
36385         
36386         return cfg;
36387         
36388     },
36389     
36390     getSplitAutoCreate : function()
36391     {
36392         var cls = 'masonry-brick masonry-brick-split';
36393         
36394         if(this.href.length){
36395             cls += ' masonry-brick-link';
36396         }
36397         
36398         if(this.bgimage.length){
36399             cls += ' masonry-brick-image';
36400         }
36401         
36402         if(this.size){
36403             cls += ' masonry-' + this.size + '-brick';
36404         }
36405         
36406         switch (this.placetitle) {
36407             case 'center' :
36408                 cls += ' masonry-center-title';
36409                 break;
36410             case 'bottom' :
36411                 cls += ' masonry-bottom-title';
36412                 break;
36413             default:
36414                 if(!this.bgimage.length){
36415                     cls += ' masonry-center-title';
36416                 }
36417
36418                 if(this.bgimage.length){
36419                     cls += ' masonry-bottom-title';
36420                 }
36421                 break;
36422         }
36423         
36424         if(this.cls){
36425             cls += ' ' + this.cls;
36426         }
36427         
36428         var cfg = {
36429             tag: (this.href.length) ? 'a' : 'div',
36430             cls: cls,
36431             cn: [
36432                 {
36433                     tag: 'div',
36434                     cls: 'masonry-brick-split-head',
36435                     cn: [
36436                         {
36437                             tag: 'div',
36438                             cls: 'masonry-brick-paragraph',
36439                             cn: []
36440                         }
36441                     ]
36442                 },
36443                 {
36444                     tag: 'div',
36445                     cls: 'masonry-brick-split-body',
36446                     cn: []
36447                 }
36448             ]
36449         };
36450         
36451         if(this.href.length){
36452             cfg.href = this.href;
36453         }
36454         
36455         if(this.title.length){
36456             cfg.cn[0].cn[0].cn.push({
36457                 tag: 'h4',
36458                 cls: 'masonry-brick-title',
36459                 html: this.title
36460             });
36461         }
36462         
36463         if(this.html.length){
36464             cfg.cn[1].cn.push({
36465                 tag: 'p',
36466                 cls: 'masonry-brick-text',
36467                 html: this.html
36468             });
36469         }
36470
36471         if(this.bgimage.length){
36472             cfg.cn[0].cn.push({
36473                 tag: 'img',
36474                 cls: 'masonry-brick-image-view',
36475                 src: this.bgimage
36476             });
36477         }
36478         
36479         if(this.videourl.length){
36480             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36481             // youtube support only?
36482             cfg.cn[0].cn.cn.push({
36483                 tag: 'iframe',
36484                 cls: 'masonry-brick-image-view',
36485                 src: vurl,
36486                 frameborder : 0,
36487                 allowfullscreen : true
36488             });
36489         }
36490         
36491         return cfg;
36492     },
36493     
36494     initEvents: function() 
36495     {
36496         switch (this.size) {
36497             case 'xs' :
36498                 this.x = 1;
36499                 this.y = 1;
36500                 break;
36501             case 'sm' :
36502                 this.x = 2;
36503                 this.y = 2;
36504                 break;
36505             case 'md' :
36506             case 'md-left' :
36507             case 'md-right' :
36508                 this.x = 3;
36509                 this.y = 3;
36510                 break;
36511             case 'tall' :
36512                 this.x = 2;
36513                 this.y = 3;
36514                 break;
36515             case 'wide' :
36516                 this.x = 3;
36517                 this.y = 2;
36518                 break;
36519             case 'wide-thin' :
36520                 this.x = 3;
36521                 this.y = 1;
36522                 break;
36523                         
36524             default :
36525                 break;
36526         }
36527         
36528         if(Roo.isTouch){
36529             this.el.on('touchstart', this.onTouchStart, this);
36530             this.el.on('touchmove', this.onTouchMove, this);
36531             this.el.on('touchend', this.onTouchEnd, this);
36532             this.el.on('contextmenu', this.onContextMenu, this);
36533         } else {
36534             this.el.on('mouseenter'  ,this.enter, this);
36535             this.el.on('mouseleave', this.leave, this);
36536             this.el.on('click', this.onClick, this);
36537         }
36538         
36539         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36540             this.parent().bricks.push(this);   
36541         }
36542         
36543     },
36544     
36545     onClick: function(e, el)
36546     {
36547         var time = this.endTimer - this.startTimer;
36548         // Roo.log(e.preventDefault());
36549         if(Roo.isTouch){
36550             if(time > 1000){
36551                 e.preventDefault();
36552                 return;
36553             }
36554         }
36555         
36556         if(!this.preventDefault){
36557             return;
36558         }
36559         
36560         e.preventDefault();
36561         
36562         if (this.activeClass != '') {
36563             this.selectBrick();
36564         }
36565         
36566         this.fireEvent('click', this, e);
36567     },
36568     
36569     enter: function(e, el)
36570     {
36571         e.preventDefault();
36572         
36573         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36574             return;
36575         }
36576         
36577         if(this.bgimage.length && this.html.length){
36578             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36579         }
36580     },
36581     
36582     leave: function(e, el)
36583     {
36584         e.preventDefault();
36585         
36586         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36587             return;
36588         }
36589         
36590         if(this.bgimage.length && this.html.length){
36591             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36592         }
36593     },
36594     
36595     onTouchStart: function(e, el)
36596     {
36597 //        e.preventDefault();
36598         
36599         this.touchmoved = false;
36600         
36601         if(!this.isFitContainer){
36602             return;
36603         }
36604         
36605         if(!this.bgimage.length || !this.html.length){
36606             return;
36607         }
36608         
36609         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36610         
36611         this.timer = new Date().getTime();
36612         
36613     },
36614     
36615     onTouchMove: function(e, el)
36616     {
36617         this.touchmoved = true;
36618     },
36619     
36620     onContextMenu : function(e,el)
36621     {
36622         e.preventDefault();
36623         e.stopPropagation();
36624         return false;
36625     },
36626     
36627     onTouchEnd: function(e, el)
36628     {
36629 //        e.preventDefault();
36630         
36631         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36632         
36633             this.leave(e,el);
36634             
36635             return;
36636         }
36637         
36638         if(!this.bgimage.length || !this.html.length){
36639             
36640             if(this.href.length){
36641                 window.location.href = this.href;
36642             }
36643             
36644             return;
36645         }
36646         
36647         if(!this.isFitContainer){
36648             return;
36649         }
36650         
36651         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36652         
36653         window.location.href = this.href;
36654     },
36655     
36656     //selection on single brick only
36657     selectBrick : function() {
36658         
36659         if (!this.parentId) {
36660             return;
36661         }
36662         
36663         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36664         var index = m.selectedBrick.indexOf(this.id);
36665         
36666         if ( index > -1) {
36667             m.selectedBrick.splice(index,1);
36668             this.el.removeClass(this.activeClass);
36669             return;
36670         }
36671         
36672         for(var i = 0; i < m.selectedBrick.length; i++) {
36673             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36674             b.el.removeClass(b.activeClass);
36675         }
36676         
36677         m.selectedBrick = [];
36678         
36679         m.selectedBrick.push(this.id);
36680         this.el.addClass(this.activeClass);
36681         return;
36682     },
36683     
36684     isSelected : function(){
36685         return this.el.hasClass(this.activeClass);
36686         
36687     }
36688 });
36689
36690 Roo.apply(Roo.bootstrap.MasonryBrick, {
36691     
36692     //groups: {},
36693     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36694      /**
36695     * register a Masonry Brick
36696     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36697     */
36698     
36699     register : function(brick)
36700     {
36701         //this.groups[brick.id] = brick;
36702         this.groups.add(brick.id, brick);
36703     },
36704     /**
36705     * fetch a  masonry brick based on the masonry brick ID
36706     * @param {string} the masonry brick to add
36707     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36708     */
36709     
36710     get: function(brick_id) 
36711     {
36712         // if (typeof(this.groups[brick_id]) == 'undefined') {
36713         //     return false;
36714         // }
36715         // return this.groups[brick_id] ;
36716         
36717         if(this.groups.key(brick_id)) {
36718             return this.groups.key(brick_id);
36719         }
36720         
36721         return false;
36722     }
36723     
36724     
36725     
36726 });
36727
36728  /*
36729  * - LGPL
36730  *
36731  * element
36732  * 
36733  */
36734
36735 /**
36736  * @class Roo.bootstrap.Brick
36737  * @extends Roo.bootstrap.Component
36738  * Bootstrap Brick class
36739  * 
36740  * @constructor
36741  * Create a new Brick
36742  * @param {Object} config The config object
36743  */
36744
36745 Roo.bootstrap.Brick = function(config){
36746     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36747     
36748     this.addEvents({
36749         // raw events
36750         /**
36751          * @event click
36752          * When a Brick is click
36753          * @param {Roo.bootstrap.Brick} this
36754          * @param {Roo.EventObject} e
36755          */
36756         "click" : true
36757     });
36758 };
36759
36760 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36761     
36762     /**
36763      * @cfg {String} title
36764      */   
36765     title : '',
36766     /**
36767      * @cfg {String} html
36768      */   
36769     html : '',
36770     /**
36771      * @cfg {String} bgimage
36772      */   
36773     bgimage : '',
36774     /**
36775      * @cfg {String} cls
36776      */   
36777     cls : '',
36778     /**
36779      * @cfg {String} href
36780      */   
36781     href : '',
36782     /**
36783      * @cfg {String} video
36784      */   
36785     video : '',
36786     /**
36787      * @cfg {Boolean} square
36788      */   
36789     square : true,
36790     
36791     getAutoCreate : function()
36792     {
36793         var cls = 'roo-brick';
36794         
36795         if(this.href.length){
36796             cls += ' roo-brick-link';
36797         }
36798         
36799         if(this.bgimage.length){
36800             cls += ' roo-brick-image';
36801         }
36802         
36803         if(!this.html.length && !this.bgimage.length){
36804             cls += ' roo-brick-center-title';
36805         }
36806         
36807         if(!this.html.length && this.bgimage.length){
36808             cls += ' roo-brick-bottom-title';
36809         }
36810         
36811         if(this.cls){
36812             cls += ' ' + this.cls;
36813         }
36814         
36815         var cfg = {
36816             tag: (this.href.length) ? 'a' : 'div',
36817             cls: cls,
36818             cn: [
36819                 {
36820                     tag: 'div',
36821                     cls: 'roo-brick-paragraph',
36822                     cn: []
36823                 }
36824             ]
36825         };
36826         
36827         if(this.href.length){
36828             cfg.href = this.href;
36829         }
36830         
36831         var cn = cfg.cn[0].cn;
36832         
36833         if(this.title.length){
36834             cn.push({
36835                 tag: 'h4',
36836                 cls: 'roo-brick-title',
36837                 html: this.title
36838             });
36839         }
36840         
36841         if(this.html.length){
36842             cn.push({
36843                 tag: 'p',
36844                 cls: 'roo-brick-text',
36845                 html: this.html
36846             });
36847         } else {
36848             cn.cls += ' hide';
36849         }
36850         
36851         if(this.bgimage.length){
36852             cfg.cn.push({
36853                 tag: 'img',
36854                 cls: 'roo-brick-image-view',
36855                 src: this.bgimage
36856             });
36857         }
36858         
36859         return cfg;
36860     },
36861     
36862     initEvents: function() 
36863     {
36864         if(this.title.length || this.html.length){
36865             this.el.on('mouseenter'  ,this.enter, this);
36866             this.el.on('mouseleave', this.leave, this);
36867         }
36868         
36869         Roo.EventManager.onWindowResize(this.resize, this); 
36870         
36871         if(this.bgimage.length){
36872             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36873             this.imageEl.on('load', this.onImageLoad, this);
36874             return;
36875         }
36876         
36877         this.resize();
36878     },
36879     
36880     onImageLoad : function()
36881     {
36882         this.resize();
36883     },
36884     
36885     resize : function()
36886     {
36887         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36888         
36889         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36890         
36891         if(this.bgimage.length){
36892             var image = this.el.select('.roo-brick-image-view', true).first();
36893             
36894             image.setWidth(paragraph.getWidth());
36895             
36896             if(this.square){
36897                 image.setHeight(paragraph.getWidth());
36898             }
36899             
36900             this.el.setHeight(image.getHeight());
36901             paragraph.setHeight(image.getHeight());
36902             
36903         }
36904         
36905     },
36906     
36907     enter: function(e, el)
36908     {
36909         e.preventDefault();
36910         
36911         if(this.bgimage.length){
36912             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36913             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36914         }
36915     },
36916     
36917     leave: function(e, el)
36918     {
36919         e.preventDefault();
36920         
36921         if(this.bgimage.length){
36922             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36923             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36924         }
36925     }
36926     
36927 });
36928
36929  
36930
36931  /*
36932  * - LGPL
36933  *
36934  * Number field 
36935  */
36936
36937 /**
36938  * @class Roo.bootstrap.NumberField
36939  * @extends Roo.bootstrap.Input
36940  * Bootstrap NumberField class
36941  * 
36942  * 
36943  * 
36944  * 
36945  * @constructor
36946  * Create a new NumberField
36947  * @param {Object} config The config object
36948  */
36949
36950 Roo.bootstrap.NumberField = function(config){
36951     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36952 };
36953
36954 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36955     
36956     /**
36957      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36958      */
36959     allowDecimals : true,
36960     /**
36961      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36962      */
36963     decimalSeparator : ".",
36964     /**
36965      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36966      */
36967     decimalPrecision : 2,
36968     /**
36969      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36970      */
36971     allowNegative : true,
36972     
36973     /**
36974      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36975      */
36976     allowZero: true,
36977     /**
36978      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36979      */
36980     minValue : Number.NEGATIVE_INFINITY,
36981     /**
36982      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36983      */
36984     maxValue : Number.MAX_VALUE,
36985     /**
36986      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36987      */
36988     minText : "The minimum value for this field is {0}",
36989     /**
36990      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36991      */
36992     maxText : "The maximum value for this field is {0}",
36993     /**
36994      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36995      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36996      */
36997     nanText : "{0} is not a valid number",
36998     /**
36999      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
37000      */
37001     thousandsDelimiter : false,
37002     /**
37003      * @cfg {String} valueAlign alignment of value
37004      */
37005     valueAlign : "left",
37006
37007     getAutoCreate : function()
37008     {
37009         var hiddenInput = {
37010             tag: 'input',
37011             type: 'hidden',
37012             id: Roo.id(),
37013             cls: 'hidden-number-input'
37014         };
37015         
37016         if (this.name) {
37017             hiddenInput.name = this.name;
37018         }
37019         
37020         this.name = '';
37021         
37022         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37023         
37024         this.name = hiddenInput.name;
37025         
37026         if(cfg.cn.length > 0) {
37027             cfg.cn.push(hiddenInput);
37028         }
37029         
37030         return cfg;
37031     },
37032
37033     // private
37034     initEvents : function()
37035     {   
37036         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37037         
37038         var allowed = "0123456789";
37039         
37040         if(this.allowDecimals){
37041             allowed += this.decimalSeparator;
37042         }
37043         
37044         if(this.allowNegative){
37045             allowed += "-";
37046         }
37047         
37048         if(this.thousandsDelimiter) {
37049             allowed += ",";
37050         }
37051         
37052         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37053         
37054         var keyPress = function(e){
37055             
37056             var k = e.getKey();
37057             
37058             var c = e.getCharCode();
37059             
37060             if(
37061                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37062                     allowed.indexOf(String.fromCharCode(c)) === -1
37063             ){
37064                 e.stopEvent();
37065                 return;
37066             }
37067             
37068             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37069                 return;
37070             }
37071             
37072             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37073                 e.stopEvent();
37074             }
37075         };
37076         
37077         this.el.on("keypress", keyPress, this);
37078     },
37079     
37080     validateValue : function(value)
37081     {
37082         
37083         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37084             return false;
37085         }
37086         
37087         var num = this.parseValue(value);
37088         
37089         if(isNaN(num)){
37090             this.markInvalid(String.format(this.nanText, value));
37091             return false;
37092         }
37093         
37094         if(num < this.minValue){
37095             this.markInvalid(String.format(this.minText, this.minValue));
37096             return false;
37097         }
37098         
37099         if(num > this.maxValue){
37100             this.markInvalid(String.format(this.maxText, this.maxValue));
37101             return false;
37102         }
37103         
37104         return true;
37105     },
37106
37107     getValue : function()
37108     {
37109         var v = this.hiddenEl().getValue();
37110         
37111         return this.fixPrecision(this.parseValue(v));
37112     },
37113
37114     parseValue : function(value)
37115     {
37116         if(this.thousandsDelimiter) {
37117             value += "";
37118             r = new RegExp(",", "g");
37119             value = value.replace(r, "");
37120         }
37121         
37122         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37123         return isNaN(value) ? '' : value;
37124     },
37125
37126     fixPrecision : function(value)
37127     {
37128         if(this.thousandsDelimiter) {
37129             value += "";
37130             r = new RegExp(",", "g");
37131             value = value.replace(r, "");
37132         }
37133         
37134         var nan = isNaN(value);
37135         
37136         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37137             return nan ? '' : value;
37138         }
37139         return parseFloat(value).toFixed(this.decimalPrecision);
37140     },
37141
37142     setValue : function(v)
37143     {
37144         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37145         
37146         this.value = v;
37147         
37148         if(this.rendered){
37149             
37150             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37151             
37152             this.inputEl().dom.value = (v == '') ? '' :
37153                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37154             
37155             if(!this.allowZero && v === '0') {
37156                 this.hiddenEl().dom.value = '';
37157                 this.inputEl().dom.value = '';
37158             }
37159             
37160             this.validate();
37161         }
37162     },
37163
37164     decimalPrecisionFcn : function(v)
37165     {
37166         return Math.floor(v);
37167     },
37168
37169     beforeBlur : function()
37170     {
37171         var v = this.parseValue(this.getRawValue());
37172         
37173         if(v || v === 0 || v === ''){
37174             this.setValue(v);
37175         }
37176     },
37177     
37178     hiddenEl : function()
37179     {
37180         return this.el.select('input.hidden-number-input',true).first();
37181     }
37182     
37183 });
37184
37185  
37186
37187 /*
37188 * Licence: LGPL
37189 */
37190
37191 /**
37192  * @class Roo.bootstrap.DocumentSlider
37193  * @extends Roo.bootstrap.Component
37194  * Bootstrap DocumentSlider class
37195  * 
37196  * @constructor
37197  * Create a new DocumentViewer
37198  * @param {Object} config The config object
37199  */
37200
37201 Roo.bootstrap.DocumentSlider = function(config){
37202     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37203     
37204     this.files = [];
37205     
37206     this.addEvents({
37207         /**
37208          * @event initial
37209          * Fire after initEvent
37210          * @param {Roo.bootstrap.DocumentSlider} this
37211          */
37212         "initial" : true,
37213         /**
37214          * @event update
37215          * Fire after update
37216          * @param {Roo.bootstrap.DocumentSlider} this
37217          */
37218         "update" : true,
37219         /**
37220          * @event click
37221          * Fire after click
37222          * @param {Roo.bootstrap.DocumentSlider} this
37223          */
37224         "click" : true
37225     });
37226 };
37227
37228 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37229     
37230     files : false,
37231     
37232     indicator : 0,
37233     
37234     getAutoCreate : function()
37235     {
37236         var cfg = {
37237             tag : 'div',
37238             cls : 'roo-document-slider',
37239             cn : [
37240                 {
37241                     tag : 'div',
37242                     cls : 'roo-document-slider-header',
37243                     cn : [
37244                         {
37245                             tag : 'div',
37246                             cls : 'roo-document-slider-header-title'
37247                         }
37248                     ]
37249                 },
37250                 {
37251                     tag : 'div',
37252                     cls : 'roo-document-slider-body',
37253                     cn : [
37254                         {
37255                             tag : 'div',
37256                             cls : 'roo-document-slider-prev',
37257                             cn : [
37258                                 {
37259                                     tag : 'i',
37260                                     cls : 'fa fa-chevron-left'
37261                                 }
37262                             ]
37263                         },
37264                         {
37265                             tag : 'div',
37266                             cls : 'roo-document-slider-thumb',
37267                             cn : [
37268                                 {
37269                                     tag : 'img',
37270                                     cls : 'roo-document-slider-image'
37271                                 }
37272                             ]
37273                         },
37274                         {
37275                             tag : 'div',
37276                             cls : 'roo-document-slider-next',
37277                             cn : [
37278                                 {
37279                                     tag : 'i',
37280                                     cls : 'fa fa-chevron-right'
37281                                 }
37282                             ]
37283                         }
37284                     ]
37285                 }
37286             ]
37287         };
37288         
37289         return cfg;
37290     },
37291     
37292     initEvents : function()
37293     {
37294         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37295         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37296         
37297         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37298         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37299         
37300         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37301         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37302         
37303         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37304         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37305         
37306         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37307         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37308         
37309         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37310         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37311         
37312         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37313         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37314         
37315         this.thumbEl.on('click', this.onClick, this);
37316         
37317         this.prevIndicator.on('click', this.prev, this);
37318         
37319         this.nextIndicator.on('click', this.next, this);
37320         
37321     },
37322     
37323     initial : function()
37324     {
37325         if(this.files.length){
37326             this.indicator = 1;
37327             this.update()
37328         }
37329         
37330         this.fireEvent('initial', this);
37331     },
37332     
37333     update : function()
37334     {
37335         this.imageEl.attr('src', this.files[this.indicator - 1]);
37336         
37337         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37338         
37339         this.prevIndicator.show();
37340         
37341         if(this.indicator == 1){
37342             this.prevIndicator.hide();
37343         }
37344         
37345         this.nextIndicator.show();
37346         
37347         if(this.indicator == this.files.length){
37348             this.nextIndicator.hide();
37349         }
37350         
37351         this.thumbEl.scrollTo('top');
37352         
37353         this.fireEvent('update', this);
37354     },
37355     
37356     onClick : function(e)
37357     {
37358         e.preventDefault();
37359         
37360         this.fireEvent('click', this);
37361     },
37362     
37363     prev : function(e)
37364     {
37365         e.preventDefault();
37366         
37367         this.indicator = Math.max(1, this.indicator - 1);
37368         
37369         this.update();
37370     },
37371     
37372     next : function(e)
37373     {
37374         e.preventDefault();
37375         
37376         this.indicator = Math.min(this.files.length, this.indicator + 1);
37377         
37378         this.update();
37379     }
37380 });
37381 /*
37382  * - LGPL
37383  *
37384  * RadioSet
37385  *
37386  *
37387  */
37388
37389 /**
37390  * @class Roo.bootstrap.RadioSet
37391  * @extends Roo.bootstrap.Input
37392  * Bootstrap RadioSet class
37393  * @cfg {String} indicatorpos (left|right) default left
37394  * @cfg {Boolean} inline (true|false) inline the element (default true)
37395  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37396  * @constructor
37397  * Create a new RadioSet
37398  * @param {Object} config The config object
37399  */
37400
37401 Roo.bootstrap.RadioSet = function(config){
37402     
37403     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37404     
37405     this.radioes = [];
37406     
37407     Roo.bootstrap.RadioSet.register(this);
37408     
37409     this.addEvents({
37410         /**
37411         * @event check
37412         * Fires when the element is checked or unchecked.
37413         * @param {Roo.bootstrap.RadioSet} this This radio
37414         * @param {Roo.bootstrap.Radio} item The checked item
37415         */
37416        check : true,
37417        /**
37418         * @event click
37419         * Fires when the element is click.
37420         * @param {Roo.bootstrap.RadioSet} this This radio set
37421         * @param {Roo.bootstrap.Radio} item The checked item
37422         * @param {Roo.EventObject} e The event object
37423         */
37424        click : true
37425     });
37426     
37427 };
37428
37429 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37430
37431     radioes : false,
37432     
37433     inline : true,
37434     
37435     weight : '',
37436     
37437     indicatorpos : 'left',
37438     
37439     getAutoCreate : function()
37440     {
37441         var label = {
37442             tag : 'label',
37443             cls : 'roo-radio-set-label',
37444             cn : [
37445                 {
37446                     tag : 'span',
37447                     html : this.fieldLabel
37448                 }
37449             ]
37450         };
37451         if (Roo.bootstrap.version == 3) {
37452             
37453             
37454             if(this.indicatorpos == 'left'){
37455                 label.cn.unshift({
37456                     tag : 'i',
37457                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37458                     tooltip : 'This field is required'
37459                 });
37460             } else {
37461                 label.cn.push({
37462                     tag : 'i',
37463                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37464                     tooltip : 'This field is required'
37465                 });
37466             }
37467         }
37468         var items = {
37469             tag : 'div',
37470             cls : 'roo-radio-set-items'
37471         };
37472         
37473         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37474         
37475         if (align === 'left' && this.fieldLabel.length) {
37476             
37477             items = {
37478                 cls : "roo-radio-set-right", 
37479                 cn: [
37480                     items
37481                 ]
37482             };
37483             
37484             if(this.labelWidth > 12){
37485                 label.style = "width: " + this.labelWidth + 'px';
37486             }
37487             
37488             if(this.labelWidth < 13 && this.labelmd == 0){
37489                 this.labelmd = this.labelWidth;
37490             }
37491             
37492             if(this.labellg > 0){
37493                 label.cls += ' col-lg-' + this.labellg;
37494                 items.cls += ' col-lg-' + (12 - this.labellg);
37495             }
37496             
37497             if(this.labelmd > 0){
37498                 label.cls += ' col-md-' + this.labelmd;
37499                 items.cls += ' col-md-' + (12 - this.labelmd);
37500             }
37501             
37502             if(this.labelsm > 0){
37503                 label.cls += ' col-sm-' + this.labelsm;
37504                 items.cls += ' col-sm-' + (12 - this.labelsm);
37505             }
37506             
37507             if(this.labelxs > 0){
37508                 label.cls += ' col-xs-' + this.labelxs;
37509                 items.cls += ' col-xs-' + (12 - this.labelxs);
37510             }
37511         }
37512         
37513         var cfg = {
37514             tag : 'div',
37515             cls : 'roo-radio-set',
37516             cn : [
37517                 {
37518                     tag : 'input',
37519                     cls : 'roo-radio-set-input',
37520                     type : 'hidden',
37521                     name : this.name,
37522                     value : this.value ? this.value :  ''
37523                 },
37524                 label,
37525                 items
37526             ]
37527         };
37528         
37529         if(this.weight.length){
37530             cfg.cls += ' roo-radio-' + this.weight;
37531         }
37532         
37533         if(this.inline) {
37534             cfg.cls += ' roo-radio-set-inline';
37535         }
37536         
37537         var settings=this;
37538         ['xs','sm','md','lg'].map(function(size){
37539             if (settings[size]) {
37540                 cfg.cls += ' col-' + size + '-' + settings[size];
37541             }
37542         });
37543         
37544         return cfg;
37545         
37546     },
37547
37548     initEvents : function()
37549     {
37550         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37551         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37552         
37553         if(!this.fieldLabel.length){
37554             this.labelEl.hide();
37555         }
37556         
37557         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37558         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37559         
37560         this.indicator = this.indicatorEl();
37561         
37562         if(this.indicator){
37563             this.indicator.addClass('invisible');
37564         }
37565         
37566         this.originalValue = this.getValue();
37567         
37568     },
37569     
37570     inputEl: function ()
37571     {
37572         return this.el.select('.roo-radio-set-input', true).first();
37573     },
37574     
37575     getChildContainer : function()
37576     {
37577         return this.itemsEl;
37578     },
37579     
37580     register : function(item)
37581     {
37582         this.radioes.push(item);
37583         
37584     },
37585     
37586     validate : function()
37587     {   
37588         if(this.getVisibilityEl().hasClass('hidden')){
37589             return true;
37590         }
37591         
37592         var valid = false;
37593         
37594         Roo.each(this.radioes, function(i){
37595             if(!i.checked){
37596                 return;
37597             }
37598             
37599             valid = true;
37600             return false;
37601         });
37602         
37603         if(this.allowBlank) {
37604             return true;
37605         }
37606         
37607         if(this.disabled || valid){
37608             this.markValid();
37609             return true;
37610         }
37611         
37612         this.markInvalid();
37613         return false;
37614         
37615     },
37616     
37617     markValid : function()
37618     {
37619         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37620             this.indicatorEl().removeClass('visible');
37621             this.indicatorEl().addClass('invisible');
37622         }
37623         
37624         
37625         if (Roo.bootstrap.version == 3) {
37626             this.el.removeClass([this.invalidClass, this.validClass]);
37627             this.el.addClass(this.validClass);
37628         } else {
37629             this.el.removeClass(['is-invalid','is-valid']);
37630             this.el.addClass(['is-valid']);
37631         }
37632         this.fireEvent('valid', this);
37633     },
37634     
37635     markInvalid : function(msg)
37636     {
37637         if(this.allowBlank || this.disabled){
37638             return;
37639         }
37640         
37641         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37642             this.indicatorEl().removeClass('invisible');
37643             this.indicatorEl().addClass('visible');
37644         }
37645         if (Roo.bootstrap.version == 3) {
37646             this.el.removeClass([this.invalidClass, this.validClass]);
37647             this.el.addClass(this.invalidClass);
37648         } else {
37649             this.el.removeClass(['is-invalid','is-valid']);
37650             this.el.addClass(['is-invalid']);
37651         }
37652         
37653         this.fireEvent('invalid', this, msg);
37654         
37655     },
37656     
37657     setValue : function(v, suppressEvent)
37658     {   
37659         if(this.value === v){
37660             return;
37661         }
37662         
37663         this.value = v;
37664         
37665         if(this.rendered){
37666             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37667         }
37668         
37669         Roo.each(this.radioes, function(i){
37670             i.checked = false;
37671             i.el.removeClass('checked');
37672         });
37673         
37674         Roo.each(this.radioes, function(i){
37675             
37676             if(i.value === v || i.value.toString() === v.toString()){
37677                 i.checked = true;
37678                 i.el.addClass('checked');
37679                 
37680                 if(suppressEvent !== true){
37681                     this.fireEvent('check', this, i);
37682                 }
37683                 
37684                 return false;
37685             }
37686             
37687         }, this);
37688         
37689         this.validate();
37690     },
37691     
37692     clearInvalid : function(){
37693         
37694         if(!this.el || this.preventMark){
37695             return;
37696         }
37697         
37698         this.el.removeClass([this.invalidClass]);
37699         
37700         this.fireEvent('valid', this);
37701     }
37702     
37703 });
37704
37705 Roo.apply(Roo.bootstrap.RadioSet, {
37706     
37707     groups: {},
37708     
37709     register : function(set)
37710     {
37711         this.groups[set.name] = set;
37712     },
37713     
37714     get: function(name) 
37715     {
37716         if (typeof(this.groups[name]) == 'undefined') {
37717             return false;
37718         }
37719         
37720         return this.groups[name] ;
37721     }
37722     
37723 });
37724 /*
37725  * Based on:
37726  * Ext JS Library 1.1.1
37727  * Copyright(c) 2006-2007, Ext JS, LLC.
37728  *
37729  * Originally Released Under LGPL - original licence link has changed is not relivant.
37730  *
37731  * Fork - LGPL
37732  * <script type="text/javascript">
37733  */
37734
37735
37736 /**
37737  * @class Roo.bootstrap.SplitBar
37738  * @extends Roo.util.Observable
37739  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37740  * <br><br>
37741  * Usage:
37742  * <pre><code>
37743 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37744                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37745 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37746 split.minSize = 100;
37747 split.maxSize = 600;
37748 split.animate = true;
37749 split.on('moved', splitterMoved);
37750 </code></pre>
37751  * @constructor
37752  * Create a new SplitBar
37753  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37754  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37755  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37756  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37757                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37758                         position of the SplitBar).
37759  */
37760 Roo.bootstrap.SplitBar = function(cfg){
37761     
37762     /** @private */
37763     
37764     //{
37765     //  dragElement : elm
37766     //  resizingElement: el,
37767         // optional..
37768     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37769     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37770         // existingProxy ???
37771     //}
37772     
37773     this.el = Roo.get(cfg.dragElement, true);
37774     this.el.dom.unselectable = "on";
37775     /** @private */
37776     this.resizingEl = Roo.get(cfg.resizingElement, true);
37777
37778     /**
37779      * @private
37780      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37781      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37782      * @type Number
37783      */
37784     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37785     
37786     /**
37787      * The minimum size of the resizing element. (Defaults to 0)
37788      * @type Number
37789      */
37790     this.minSize = 0;
37791     
37792     /**
37793      * The maximum size of the resizing element. (Defaults to 2000)
37794      * @type Number
37795      */
37796     this.maxSize = 2000;
37797     
37798     /**
37799      * Whether to animate the transition to the new size
37800      * @type Boolean
37801      */
37802     this.animate = false;
37803     
37804     /**
37805      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37806      * @type Boolean
37807      */
37808     this.useShim = false;
37809     
37810     /** @private */
37811     this.shim = null;
37812     
37813     if(!cfg.existingProxy){
37814         /** @private */
37815         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37816     }else{
37817         this.proxy = Roo.get(cfg.existingProxy).dom;
37818     }
37819     /** @private */
37820     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37821     
37822     /** @private */
37823     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37824     
37825     /** @private */
37826     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37827     
37828     /** @private */
37829     this.dragSpecs = {};
37830     
37831     /**
37832      * @private The adapter to use to positon and resize elements
37833      */
37834     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37835     this.adapter.init(this);
37836     
37837     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37838         /** @private */
37839         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37840         this.el.addClass("roo-splitbar-h");
37841     }else{
37842         /** @private */
37843         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37844         this.el.addClass("roo-splitbar-v");
37845     }
37846     
37847     this.addEvents({
37848         /**
37849          * @event resize
37850          * Fires when the splitter is moved (alias for {@link #event-moved})
37851          * @param {Roo.bootstrap.SplitBar} this
37852          * @param {Number} newSize the new width or height
37853          */
37854         "resize" : true,
37855         /**
37856          * @event moved
37857          * Fires when the splitter is moved
37858          * @param {Roo.bootstrap.SplitBar} this
37859          * @param {Number} newSize the new width or height
37860          */
37861         "moved" : true,
37862         /**
37863          * @event beforeresize
37864          * Fires before the splitter is dragged
37865          * @param {Roo.bootstrap.SplitBar} this
37866          */
37867         "beforeresize" : true,
37868
37869         "beforeapply" : true
37870     });
37871
37872     Roo.util.Observable.call(this);
37873 };
37874
37875 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37876     onStartProxyDrag : function(x, y){
37877         this.fireEvent("beforeresize", this);
37878         if(!this.overlay){
37879             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37880             o.unselectable();
37881             o.enableDisplayMode("block");
37882             // all splitbars share the same overlay
37883             Roo.bootstrap.SplitBar.prototype.overlay = o;
37884         }
37885         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37886         this.overlay.show();
37887         Roo.get(this.proxy).setDisplayed("block");
37888         var size = this.adapter.getElementSize(this);
37889         this.activeMinSize = this.getMinimumSize();;
37890         this.activeMaxSize = this.getMaximumSize();;
37891         var c1 = size - this.activeMinSize;
37892         var c2 = Math.max(this.activeMaxSize - size, 0);
37893         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37894             this.dd.resetConstraints();
37895             this.dd.setXConstraint(
37896                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37897                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37898             );
37899             this.dd.setYConstraint(0, 0);
37900         }else{
37901             this.dd.resetConstraints();
37902             this.dd.setXConstraint(0, 0);
37903             this.dd.setYConstraint(
37904                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37905                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37906             );
37907          }
37908         this.dragSpecs.startSize = size;
37909         this.dragSpecs.startPoint = [x, y];
37910         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37911     },
37912     
37913     /** 
37914      * @private Called after the drag operation by the DDProxy
37915      */
37916     onEndProxyDrag : function(e){
37917         Roo.get(this.proxy).setDisplayed(false);
37918         var endPoint = Roo.lib.Event.getXY(e);
37919         if(this.overlay){
37920             this.overlay.hide();
37921         }
37922         var newSize;
37923         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37924             newSize = this.dragSpecs.startSize + 
37925                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37926                     endPoint[0] - this.dragSpecs.startPoint[0] :
37927                     this.dragSpecs.startPoint[0] - endPoint[0]
37928                 );
37929         }else{
37930             newSize = this.dragSpecs.startSize + 
37931                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37932                     endPoint[1] - this.dragSpecs.startPoint[1] :
37933                     this.dragSpecs.startPoint[1] - endPoint[1]
37934                 );
37935         }
37936         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37937         if(newSize != this.dragSpecs.startSize){
37938             if(this.fireEvent('beforeapply', this, newSize) !== false){
37939                 this.adapter.setElementSize(this, newSize);
37940                 this.fireEvent("moved", this, newSize);
37941                 this.fireEvent("resize", this, newSize);
37942             }
37943         }
37944     },
37945     
37946     /**
37947      * Get the adapter this SplitBar uses
37948      * @return The adapter object
37949      */
37950     getAdapter : function(){
37951         return this.adapter;
37952     },
37953     
37954     /**
37955      * Set the adapter this SplitBar uses
37956      * @param {Object} adapter A SplitBar adapter object
37957      */
37958     setAdapter : function(adapter){
37959         this.adapter = adapter;
37960         this.adapter.init(this);
37961     },
37962     
37963     /**
37964      * Gets the minimum size for the resizing element
37965      * @return {Number} The minimum size
37966      */
37967     getMinimumSize : function(){
37968         return this.minSize;
37969     },
37970     
37971     /**
37972      * Sets the minimum size for the resizing element
37973      * @param {Number} minSize The minimum size
37974      */
37975     setMinimumSize : function(minSize){
37976         this.minSize = minSize;
37977     },
37978     
37979     /**
37980      * Gets the maximum size for the resizing element
37981      * @return {Number} The maximum size
37982      */
37983     getMaximumSize : function(){
37984         return this.maxSize;
37985     },
37986     
37987     /**
37988      * Sets the maximum size for the resizing element
37989      * @param {Number} maxSize The maximum size
37990      */
37991     setMaximumSize : function(maxSize){
37992         this.maxSize = maxSize;
37993     },
37994     
37995     /**
37996      * Sets the initialize size for the resizing element
37997      * @param {Number} size The initial size
37998      */
37999     setCurrentSize : function(size){
38000         var oldAnimate = this.animate;
38001         this.animate = false;
38002         this.adapter.setElementSize(this, size);
38003         this.animate = oldAnimate;
38004     },
38005     
38006     /**
38007      * Destroy this splitbar. 
38008      * @param {Boolean} removeEl True to remove the element
38009      */
38010     destroy : function(removeEl){
38011         if(this.shim){
38012             this.shim.remove();
38013         }
38014         this.dd.unreg();
38015         this.proxy.parentNode.removeChild(this.proxy);
38016         if(removeEl){
38017             this.el.remove();
38018         }
38019     }
38020 });
38021
38022 /**
38023  * @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.
38024  */
38025 Roo.bootstrap.SplitBar.createProxy = function(dir){
38026     var proxy = new Roo.Element(document.createElement("div"));
38027     proxy.unselectable();
38028     var cls = 'roo-splitbar-proxy';
38029     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38030     document.body.appendChild(proxy.dom);
38031     return proxy.dom;
38032 };
38033
38034 /** 
38035  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38036  * Default Adapter. It assumes the splitter and resizing element are not positioned
38037  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38038  */
38039 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38040 };
38041
38042 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38043     // do nothing for now
38044     init : function(s){
38045     
38046     },
38047     /**
38048      * Called before drag operations to get the current size of the resizing element. 
38049      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38050      */
38051      getElementSize : function(s){
38052         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38053             return s.resizingEl.getWidth();
38054         }else{
38055             return s.resizingEl.getHeight();
38056         }
38057     },
38058     
38059     /**
38060      * Called after drag operations to set the size of the resizing element.
38061      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38062      * @param {Number} newSize The new size to set
38063      * @param {Function} onComplete A function to be invoked when resizing is complete
38064      */
38065     setElementSize : function(s, newSize, onComplete){
38066         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38067             if(!s.animate){
38068                 s.resizingEl.setWidth(newSize);
38069                 if(onComplete){
38070                     onComplete(s, newSize);
38071                 }
38072             }else{
38073                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38074             }
38075         }else{
38076             
38077             if(!s.animate){
38078                 s.resizingEl.setHeight(newSize);
38079                 if(onComplete){
38080                     onComplete(s, newSize);
38081                 }
38082             }else{
38083                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38084             }
38085         }
38086     }
38087 };
38088
38089 /** 
38090  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38091  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38092  * Adapter that  moves the splitter element to align with the resized sizing element. 
38093  * Used with an absolute positioned SplitBar.
38094  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38095  * document.body, make sure you assign an id to the body element.
38096  */
38097 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38098     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38099     this.container = Roo.get(container);
38100 };
38101
38102 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38103     init : function(s){
38104         this.basic.init(s);
38105     },
38106     
38107     getElementSize : function(s){
38108         return this.basic.getElementSize(s);
38109     },
38110     
38111     setElementSize : function(s, newSize, onComplete){
38112         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38113     },
38114     
38115     moveSplitter : function(s){
38116         var yes = Roo.bootstrap.SplitBar;
38117         switch(s.placement){
38118             case yes.LEFT:
38119                 s.el.setX(s.resizingEl.getRight());
38120                 break;
38121             case yes.RIGHT:
38122                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38123                 break;
38124             case yes.TOP:
38125                 s.el.setY(s.resizingEl.getBottom());
38126                 break;
38127             case yes.BOTTOM:
38128                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38129                 break;
38130         }
38131     }
38132 };
38133
38134 /**
38135  * Orientation constant - Create a vertical SplitBar
38136  * @static
38137  * @type Number
38138  */
38139 Roo.bootstrap.SplitBar.VERTICAL = 1;
38140
38141 /**
38142  * Orientation constant - Create a horizontal SplitBar
38143  * @static
38144  * @type Number
38145  */
38146 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38147
38148 /**
38149  * Placement constant - The resizing element is to the left of the splitter element
38150  * @static
38151  * @type Number
38152  */
38153 Roo.bootstrap.SplitBar.LEFT = 1;
38154
38155 /**
38156  * Placement constant - The resizing element is to the right of the splitter element
38157  * @static
38158  * @type Number
38159  */
38160 Roo.bootstrap.SplitBar.RIGHT = 2;
38161
38162 /**
38163  * Placement constant - The resizing element is positioned above the splitter element
38164  * @static
38165  * @type Number
38166  */
38167 Roo.bootstrap.SplitBar.TOP = 3;
38168
38169 /**
38170  * Placement constant - The resizing element is positioned under splitter element
38171  * @static
38172  * @type Number
38173  */
38174 Roo.bootstrap.SplitBar.BOTTOM = 4;
38175 Roo.namespace("Roo.bootstrap.layout");/*
38176  * Based on:
38177  * Ext JS Library 1.1.1
38178  * Copyright(c) 2006-2007, Ext JS, LLC.
38179  *
38180  * Originally Released Under LGPL - original licence link has changed is not relivant.
38181  *
38182  * Fork - LGPL
38183  * <script type="text/javascript">
38184  */
38185
38186 /**
38187  * @class Roo.bootstrap.layout.Manager
38188  * @extends Roo.bootstrap.Component
38189  * Base class for layout managers.
38190  */
38191 Roo.bootstrap.layout.Manager = function(config)
38192 {
38193     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38194
38195
38196
38197
38198
38199     /** false to disable window resize monitoring @type Boolean */
38200     this.monitorWindowResize = true;
38201     this.regions = {};
38202     this.addEvents({
38203         /**
38204          * @event layout
38205          * Fires when a layout is performed.
38206          * @param {Roo.LayoutManager} this
38207          */
38208         "layout" : true,
38209         /**
38210          * @event regionresized
38211          * Fires when the user resizes a region.
38212          * @param {Roo.LayoutRegion} region The resized region
38213          * @param {Number} newSize The new size (width for east/west, height for north/south)
38214          */
38215         "regionresized" : true,
38216         /**
38217          * @event regioncollapsed
38218          * Fires when a region is collapsed.
38219          * @param {Roo.LayoutRegion} region The collapsed region
38220          */
38221         "regioncollapsed" : true,
38222         /**
38223          * @event regionexpanded
38224          * Fires when a region is expanded.
38225          * @param {Roo.LayoutRegion} region The expanded region
38226          */
38227         "regionexpanded" : true
38228     });
38229     this.updating = false;
38230
38231     if (config.el) {
38232         this.el = Roo.get(config.el);
38233         this.initEvents();
38234     }
38235
38236 };
38237
38238 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38239
38240
38241     regions : null,
38242
38243     monitorWindowResize : true,
38244
38245
38246     updating : false,
38247
38248
38249     onRender : function(ct, position)
38250     {
38251         if(!this.el){
38252             this.el = Roo.get(ct);
38253             this.initEvents();
38254         }
38255         //this.fireEvent('render',this);
38256     },
38257
38258
38259     initEvents: function()
38260     {
38261
38262
38263         // ie scrollbar fix
38264         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38265             document.body.scroll = "no";
38266         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38267             this.el.position('relative');
38268         }
38269         this.id = this.el.id;
38270         this.el.addClass("roo-layout-container");
38271         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38272         if(this.el.dom != document.body ) {
38273             this.el.on('resize', this.layout,this);
38274             this.el.on('show', this.layout,this);
38275         }
38276
38277     },
38278
38279     /**
38280      * Returns true if this layout is currently being updated
38281      * @return {Boolean}
38282      */
38283     isUpdating : function(){
38284         return this.updating;
38285     },
38286
38287     /**
38288      * Suspend the LayoutManager from doing auto-layouts while
38289      * making multiple add or remove calls
38290      */
38291     beginUpdate : function(){
38292         this.updating = true;
38293     },
38294
38295     /**
38296      * Restore auto-layouts and optionally disable the manager from performing a layout
38297      * @param {Boolean} noLayout true to disable a layout update
38298      */
38299     endUpdate : function(noLayout){
38300         this.updating = false;
38301         if(!noLayout){
38302             this.layout();
38303         }
38304     },
38305
38306     layout: function(){
38307         // abstract...
38308     },
38309
38310     onRegionResized : function(region, newSize){
38311         this.fireEvent("regionresized", region, newSize);
38312         this.layout();
38313     },
38314
38315     onRegionCollapsed : function(region){
38316         this.fireEvent("regioncollapsed", region);
38317     },
38318
38319     onRegionExpanded : function(region){
38320         this.fireEvent("regionexpanded", region);
38321     },
38322
38323     /**
38324      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38325      * performs box-model adjustments.
38326      * @return {Object} The size as an object {width: (the width), height: (the height)}
38327      */
38328     getViewSize : function()
38329     {
38330         var size;
38331         if(this.el.dom != document.body){
38332             size = this.el.getSize();
38333         }else{
38334             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38335         }
38336         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38337         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38338         return size;
38339     },
38340
38341     /**
38342      * Returns the Element this layout is bound to.
38343      * @return {Roo.Element}
38344      */
38345     getEl : function(){
38346         return this.el;
38347     },
38348
38349     /**
38350      * Returns the specified region.
38351      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38352      * @return {Roo.LayoutRegion}
38353      */
38354     getRegion : function(target){
38355         return this.regions[target.toLowerCase()];
38356     },
38357
38358     onWindowResize : function(){
38359         if(this.monitorWindowResize){
38360             this.layout();
38361         }
38362     }
38363 });
38364 /*
38365  * Based on:
38366  * Ext JS Library 1.1.1
38367  * Copyright(c) 2006-2007, Ext JS, LLC.
38368  *
38369  * Originally Released Under LGPL - original licence link has changed is not relivant.
38370  *
38371  * Fork - LGPL
38372  * <script type="text/javascript">
38373  */
38374 /**
38375  * @class Roo.bootstrap.layout.Border
38376  * @extends Roo.bootstrap.layout.Manager
38377  * @builder-top
38378  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38379  * please see: examples/bootstrap/nested.html<br><br>
38380  
38381 <b>The container the layout is rendered into can be either the body element or any other element.
38382 If it is not the body element, the container needs to either be an absolute positioned element,
38383 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38384 the container size if it is not the body element.</b>
38385
38386 * @constructor
38387 * Create a new Border
38388 * @param {Object} config Configuration options
38389  */
38390 Roo.bootstrap.layout.Border = function(config){
38391     config = config || {};
38392     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38393     
38394     
38395     
38396     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38397         if(config[region]){
38398             config[region].region = region;
38399             this.addRegion(config[region]);
38400         }
38401     },this);
38402     
38403 };
38404
38405 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38406
38407 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38408     
38409     parent : false, // this might point to a 'nest' or a ???
38410     
38411     /**
38412      * Creates and adds a new region if it doesn't already exist.
38413      * @param {String} target The target region key (north, south, east, west or center).
38414      * @param {Object} config The regions config object
38415      * @return {BorderLayoutRegion} The new region
38416      */
38417     addRegion : function(config)
38418     {
38419         if(!this.regions[config.region]){
38420             var r = this.factory(config);
38421             this.bindRegion(r);
38422         }
38423         return this.regions[config.region];
38424     },
38425
38426     // private (kinda)
38427     bindRegion : function(r){
38428         this.regions[r.config.region] = r;
38429         
38430         r.on("visibilitychange",    this.layout, this);
38431         r.on("paneladded",          this.layout, this);
38432         r.on("panelremoved",        this.layout, this);
38433         r.on("invalidated",         this.layout, this);
38434         r.on("resized",             this.onRegionResized, this);
38435         r.on("collapsed",           this.onRegionCollapsed, this);
38436         r.on("expanded",            this.onRegionExpanded, this);
38437     },
38438
38439     /**
38440      * Performs a layout update.
38441      */
38442     layout : function()
38443     {
38444         if(this.updating) {
38445             return;
38446         }
38447         
38448         // render all the rebions if they have not been done alreayd?
38449         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38450             if(this.regions[region] && !this.regions[region].bodyEl){
38451                 this.regions[region].onRender(this.el)
38452             }
38453         },this);
38454         
38455         var size = this.getViewSize();
38456         var w = size.width;
38457         var h = size.height;
38458         var centerW = w;
38459         var centerH = h;
38460         var centerY = 0;
38461         var centerX = 0;
38462         //var x = 0, y = 0;
38463
38464         var rs = this.regions;
38465         var north = rs["north"];
38466         var south = rs["south"]; 
38467         var west = rs["west"];
38468         var east = rs["east"];
38469         var center = rs["center"];
38470         //if(this.hideOnLayout){ // not supported anymore
38471             //c.el.setStyle("display", "none");
38472         //}
38473         if(north && north.isVisible()){
38474             var b = north.getBox();
38475             var m = north.getMargins();
38476             b.width = w - (m.left+m.right);
38477             b.x = m.left;
38478             b.y = m.top;
38479             centerY = b.height + b.y + m.bottom;
38480             centerH -= centerY;
38481             north.updateBox(this.safeBox(b));
38482         }
38483         if(south && south.isVisible()){
38484             var b = south.getBox();
38485             var m = south.getMargins();
38486             b.width = w - (m.left+m.right);
38487             b.x = m.left;
38488             var totalHeight = (b.height + m.top + m.bottom);
38489             b.y = h - totalHeight + m.top;
38490             centerH -= totalHeight;
38491             south.updateBox(this.safeBox(b));
38492         }
38493         if(west && west.isVisible()){
38494             var b = west.getBox();
38495             var m = west.getMargins();
38496             b.height = centerH - (m.top+m.bottom);
38497             b.x = m.left;
38498             b.y = centerY + m.top;
38499             var totalWidth = (b.width + m.left + m.right);
38500             centerX += totalWidth;
38501             centerW -= totalWidth;
38502             west.updateBox(this.safeBox(b));
38503         }
38504         if(east && east.isVisible()){
38505             var b = east.getBox();
38506             var m = east.getMargins();
38507             b.height = centerH - (m.top+m.bottom);
38508             var totalWidth = (b.width + m.left + m.right);
38509             b.x = w - totalWidth + m.left;
38510             b.y = centerY + m.top;
38511             centerW -= totalWidth;
38512             east.updateBox(this.safeBox(b));
38513         }
38514         if(center){
38515             var m = center.getMargins();
38516             var centerBox = {
38517                 x: centerX + m.left,
38518                 y: centerY + m.top,
38519                 width: centerW - (m.left+m.right),
38520                 height: centerH - (m.top+m.bottom)
38521             };
38522             //if(this.hideOnLayout){
38523                 //center.el.setStyle("display", "block");
38524             //}
38525             center.updateBox(this.safeBox(centerBox));
38526         }
38527         this.el.repaint();
38528         this.fireEvent("layout", this);
38529     },
38530
38531     // private
38532     safeBox : function(box){
38533         box.width = Math.max(0, box.width);
38534         box.height = Math.max(0, box.height);
38535         return box;
38536     },
38537
38538     /**
38539      * Adds a ContentPanel (or subclass) to this layout.
38540      * @param {String} target The target region key (north, south, east, west or center).
38541      * @param {Roo.ContentPanel} panel The panel to add
38542      * @return {Roo.ContentPanel} The added panel
38543      */
38544     add : function(target, panel){
38545          
38546         target = target.toLowerCase();
38547         return this.regions[target].add(panel);
38548     },
38549
38550     /**
38551      * Remove a ContentPanel (or subclass) to this layout.
38552      * @param {String} target The target region key (north, south, east, west or center).
38553      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38554      * @return {Roo.ContentPanel} The removed panel
38555      */
38556     remove : function(target, panel){
38557         target = target.toLowerCase();
38558         return this.regions[target].remove(panel);
38559     },
38560
38561     /**
38562      * Searches all regions for a panel with the specified id
38563      * @param {String} panelId
38564      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38565      */
38566     findPanel : function(panelId){
38567         var rs = this.regions;
38568         for(var target in rs){
38569             if(typeof rs[target] != "function"){
38570                 var p = rs[target].getPanel(panelId);
38571                 if(p){
38572                     return p;
38573                 }
38574             }
38575         }
38576         return null;
38577     },
38578
38579     /**
38580      * Searches all regions for a panel with the specified id and activates (shows) it.
38581      * @param {String/ContentPanel} panelId The panels id or the panel itself
38582      * @return {Roo.ContentPanel} The shown panel or null
38583      */
38584     showPanel : function(panelId) {
38585       var rs = this.regions;
38586       for(var target in rs){
38587          var r = rs[target];
38588          if(typeof r != "function"){
38589             if(r.hasPanel(panelId)){
38590                return r.showPanel(panelId);
38591             }
38592          }
38593       }
38594       return null;
38595    },
38596
38597    /**
38598      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38599      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38600      */
38601    /*
38602     restoreState : function(provider){
38603         if(!provider){
38604             provider = Roo.state.Manager;
38605         }
38606         var sm = new Roo.LayoutStateManager();
38607         sm.init(this, provider);
38608     },
38609 */
38610  
38611  
38612     /**
38613      * Adds a xtype elements to the layout.
38614      * <pre><code>
38615
38616 layout.addxtype({
38617        xtype : 'ContentPanel',
38618        region: 'west',
38619        items: [ .... ]
38620    }
38621 );
38622
38623 layout.addxtype({
38624         xtype : 'NestedLayoutPanel',
38625         region: 'west',
38626         layout: {
38627            center: { },
38628            west: { }   
38629         },
38630         items : [ ... list of content panels or nested layout panels.. ]
38631    }
38632 );
38633 </code></pre>
38634      * @param {Object} cfg Xtype definition of item to add.
38635      */
38636     addxtype : function(cfg)
38637     {
38638         // basically accepts a pannel...
38639         // can accept a layout region..!?!?
38640         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38641         
38642         
38643         // theory?  children can only be panels??
38644         
38645         //if (!cfg.xtype.match(/Panel$/)) {
38646         //    return false;
38647         //}
38648         var ret = false;
38649         
38650         if (typeof(cfg.region) == 'undefined') {
38651             Roo.log("Failed to add Panel, region was not set");
38652             Roo.log(cfg);
38653             return false;
38654         }
38655         var region = cfg.region;
38656         delete cfg.region;
38657         
38658           
38659         var xitems = [];
38660         if (cfg.items) {
38661             xitems = cfg.items;
38662             delete cfg.items;
38663         }
38664         var nb = false;
38665         
38666         if ( region == 'center') {
38667             Roo.log("Center: " + cfg.title);
38668         }
38669         
38670         
38671         switch(cfg.xtype) 
38672         {
38673             case 'Content':  // ContentPanel (el, cfg)
38674             case 'Scroll':  // ContentPanel (el, cfg)
38675             case 'View': 
38676                 cfg.autoCreate = cfg.autoCreate || true;
38677                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38678                 //} else {
38679                 //    var el = this.el.createChild();
38680                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38681                 //}
38682                 
38683                 this.add(region, ret);
38684                 break;
38685             
38686             /*
38687             case 'TreePanel': // our new panel!
38688                 cfg.el = this.el.createChild();
38689                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38690                 this.add(region, ret);
38691                 break;
38692             */
38693             
38694             case 'Nest': 
38695                 // create a new Layout (which is  a Border Layout...
38696                 
38697                 var clayout = cfg.layout;
38698                 clayout.el  = this.el.createChild();
38699                 clayout.items   = clayout.items  || [];
38700                 
38701                 delete cfg.layout;
38702                 
38703                 // replace this exitems with the clayout ones..
38704                 xitems = clayout.items;
38705                  
38706                 // force background off if it's in center...
38707                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38708                     cfg.background = false;
38709                 }
38710                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38711                 
38712                 
38713                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38714                 //console.log('adding nested layout panel '  + cfg.toSource());
38715                 this.add(region, ret);
38716                 nb = {}; /// find first...
38717                 break;
38718             
38719             case 'Grid':
38720                 
38721                 // needs grid and region
38722                 
38723                 //var el = this.getRegion(region).el.createChild();
38724                 /*
38725                  *var el = this.el.createChild();
38726                 // create the grid first...
38727                 cfg.grid.container = el;
38728                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38729                 */
38730                 
38731                 if (region == 'center' && this.active ) {
38732                     cfg.background = false;
38733                 }
38734                 
38735                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38736                 
38737                 this.add(region, ret);
38738                 /*
38739                 if (cfg.background) {
38740                     // render grid on panel activation (if panel background)
38741                     ret.on('activate', function(gp) {
38742                         if (!gp.grid.rendered) {
38743                     //        gp.grid.render(el);
38744                         }
38745                     });
38746                 } else {
38747                   //  cfg.grid.render(el);
38748                 }
38749                 */
38750                 break;
38751            
38752            
38753             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38754                 // it was the old xcomponent building that caused this before.
38755                 // espeically if border is the top element in the tree.
38756                 ret = this;
38757                 break; 
38758                 
38759                     
38760                 
38761                 
38762                 
38763             default:
38764                 /*
38765                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38766                     
38767                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38768                     this.add(region, ret);
38769                 } else {
38770                 */
38771                     Roo.log(cfg);
38772                     throw "Can not add '" + cfg.xtype + "' to Border";
38773                     return null;
38774              
38775                                 
38776              
38777         }
38778         this.beginUpdate();
38779         // add children..
38780         var region = '';
38781         var abn = {};
38782         Roo.each(xitems, function(i)  {
38783             region = nb && i.region ? i.region : false;
38784             
38785             var add = ret.addxtype(i);
38786            
38787             if (region) {
38788                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38789                 if (!i.background) {
38790                     abn[region] = nb[region] ;
38791                 }
38792             }
38793             
38794         });
38795         this.endUpdate();
38796
38797         // make the last non-background panel active..
38798         //if (nb) { Roo.log(abn); }
38799         if (nb) {
38800             
38801             for(var r in abn) {
38802                 region = this.getRegion(r);
38803                 if (region) {
38804                     // tried using nb[r], but it does not work..
38805                      
38806                     region.showPanel(abn[r]);
38807                    
38808                 }
38809             }
38810         }
38811         return ret;
38812         
38813     },
38814     
38815     
38816 // private
38817     factory : function(cfg)
38818     {
38819         
38820         var validRegions = Roo.bootstrap.layout.Border.regions;
38821
38822         var target = cfg.region;
38823         cfg.mgr = this;
38824         
38825         var r = Roo.bootstrap.layout;
38826         Roo.log(target);
38827         switch(target){
38828             case "north":
38829                 return new r.North(cfg);
38830             case "south":
38831                 return new r.South(cfg);
38832             case "east":
38833                 return new r.East(cfg);
38834             case "west":
38835                 return new r.West(cfg);
38836             case "center":
38837                 return new r.Center(cfg);
38838         }
38839         throw 'Layout region "'+target+'" not supported.';
38840     }
38841     
38842     
38843 });
38844  /*
38845  * Based on:
38846  * Ext JS Library 1.1.1
38847  * Copyright(c) 2006-2007, Ext JS, LLC.
38848  *
38849  * Originally Released Under LGPL - original licence link has changed is not relivant.
38850  *
38851  * Fork - LGPL
38852  * <script type="text/javascript">
38853  */
38854  
38855 /**
38856  * @class Roo.bootstrap.layout.Basic
38857  * @extends Roo.util.Observable
38858  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38859  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38860  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38861  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38862  * @cfg {string}   region  the region that it inhabits..
38863  * @cfg {bool}   skipConfig skip config?
38864  * 
38865
38866  */
38867 Roo.bootstrap.layout.Basic = function(config){
38868     
38869     this.mgr = config.mgr;
38870     
38871     this.position = config.region;
38872     
38873     var skipConfig = config.skipConfig;
38874     
38875     this.events = {
38876         /**
38877          * @scope Roo.BasicLayoutRegion
38878          */
38879         
38880         /**
38881          * @event beforeremove
38882          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38883          * @param {Roo.LayoutRegion} this
38884          * @param {Roo.ContentPanel} panel The panel
38885          * @param {Object} e The cancel event object
38886          */
38887         "beforeremove" : true,
38888         /**
38889          * @event invalidated
38890          * Fires when the layout for this region is changed.
38891          * @param {Roo.LayoutRegion} this
38892          */
38893         "invalidated" : true,
38894         /**
38895          * @event visibilitychange
38896          * Fires when this region is shown or hidden 
38897          * @param {Roo.LayoutRegion} this
38898          * @param {Boolean} visibility true or false
38899          */
38900         "visibilitychange" : true,
38901         /**
38902          * @event paneladded
38903          * Fires when a panel is added. 
38904          * @param {Roo.LayoutRegion} this
38905          * @param {Roo.ContentPanel} panel The panel
38906          */
38907         "paneladded" : true,
38908         /**
38909          * @event panelremoved
38910          * Fires when a panel is removed. 
38911          * @param {Roo.LayoutRegion} this
38912          * @param {Roo.ContentPanel} panel The panel
38913          */
38914         "panelremoved" : true,
38915         /**
38916          * @event beforecollapse
38917          * Fires when this region before collapse.
38918          * @param {Roo.LayoutRegion} this
38919          */
38920         "beforecollapse" : true,
38921         /**
38922          * @event collapsed
38923          * Fires when this region is collapsed.
38924          * @param {Roo.LayoutRegion} this
38925          */
38926         "collapsed" : true,
38927         /**
38928          * @event expanded
38929          * Fires when this region is expanded.
38930          * @param {Roo.LayoutRegion} this
38931          */
38932         "expanded" : true,
38933         /**
38934          * @event slideshow
38935          * Fires when this region is slid into view.
38936          * @param {Roo.LayoutRegion} this
38937          */
38938         "slideshow" : true,
38939         /**
38940          * @event slidehide
38941          * Fires when this region slides out of view. 
38942          * @param {Roo.LayoutRegion} this
38943          */
38944         "slidehide" : true,
38945         /**
38946          * @event panelactivated
38947          * Fires when a panel is activated. 
38948          * @param {Roo.LayoutRegion} this
38949          * @param {Roo.ContentPanel} panel The activated panel
38950          */
38951         "panelactivated" : true,
38952         /**
38953          * @event resized
38954          * Fires when the user resizes this region. 
38955          * @param {Roo.LayoutRegion} this
38956          * @param {Number} newSize The new size (width for east/west, height for north/south)
38957          */
38958         "resized" : true
38959     };
38960     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38961     this.panels = new Roo.util.MixedCollection();
38962     this.panels.getKey = this.getPanelId.createDelegate(this);
38963     this.box = null;
38964     this.activePanel = null;
38965     // ensure listeners are added...
38966     
38967     if (config.listeners || config.events) {
38968         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38969             listeners : config.listeners || {},
38970             events : config.events || {}
38971         });
38972     }
38973     
38974     if(skipConfig !== true){
38975         this.applyConfig(config);
38976     }
38977 };
38978
38979 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38980 {
38981     getPanelId : function(p){
38982         return p.getId();
38983     },
38984     
38985     applyConfig : function(config){
38986         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38987         this.config = config;
38988         
38989     },
38990     
38991     /**
38992      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38993      * the width, for horizontal (north, south) the height.
38994      * @param {Number} newSize The new width or height
38995      */
38996     resizeTo : function(newSize){
38997         var el = this.el ? this.el :
38998                  (this.activePanel ? this.activePanel.getEl() : null);
38999         if(el){
39000             switch(this.position){
39001                 case "east":
39002                 case "west":
39003                     el.setWidth(newSize);
39004                     this.fireEvent("resized", this, newSize);
39005                 break;
39006                 case "north":
39007                 case "south":
39008                     el.setHeight(newSize);
39009                     this.fireEvent("resized", this, newSize);
39010                 break;                
39011             }
39012         }
39013     },
39014     
39015     getBox : function(){
39016         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39017     },
39018     
39019     getMargins : function(){
39020         return this.margins;
39021     },
39022     
39023     updateBox : function(box){
39024         this.box = box;
39025         var el = this.activePanel.getEl();
39026         el.dom.style.left = box.x + "px";
39027         el.dom.style.top = box.y + "px";
39028         this.activePanel.setSize(box.width, box.height);
39029     },
39030     
39031     /**
39032      * Returns the container element for this region.
39033      * @return {Roo.Element}
39034      */
39035     getEl : function(){
39036         return this.activePanel;
39037     },
39038     
39039     /**
39040      * Returns true if this region is currently visible.
39041      * @return {Boolean}
39042      */
39043     isVisible : function(){
39044         return this.activePanel ? true : false;
39045     },
39046     
39047     setActivePanel : function(panel){
39048         panel = this.getPanel(panel);
39049         if(this.activePanel && this.activePanel != panel){
39050             this.activePanel.setActiveState(false);
39051             this.activePanel.getEl().setLeftTop(-10000,-10000);
39052         }
39053         this.activePanel = panel;
39054         panel.setActiveState(true);
39055         if(this.box){
39056             panel.setSize(this.box.width, this.box.height);
39057         }
39058         this.fireEvent("panelactivated", this, panel);
39059         this.fireEvent("invalidated");
39060     },
39061     
39062     /**
39063      * Show the specified panel.
39064      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39065      * @return {Roo.ContentPanel} The shown panel or null
39066      */
39067     showPanel : function(panel){
39068         panel = this.getPanel(panel);
39069         if(panel){
39070             this.setActivePanel(panel);
39071         }
39072         return panel;
39073     },
39074     
39075     /**
39076      * Get the active panel for this region.
39077      * @return {Roo.ContentPanel} The active panel or null
39078      */
39079     getActivePanel : function(){
39080         return this.activePanel;
39081     },
39082     
39083     /**
39084      * Add the passed ContentPanel(s)
39085      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39086      * @return {Roo.ContentPanel} The panel added (if only one was added)
39087      */
39088     add : function(panel){
39089         if(arguments.length > 1){
39090             for(var i = 0, len = arguments.length; i < len; i++) {
39091                 this.add(arguments[i]);
39092             }
39093             return null;
39094         }
39095         if(this.hasPanel(panel)){
39096             this.showPanel(panel);
39097             return panel;
39098         }
39099         var el = panel.getEl();
39100         if(el.dom.parentNode != this.mgr.el.dom){
39101             this.mgr.el.dom.appendChild(el.dom);
39102         }
39103         if(panel.setRegion){
39104             panel.setRegion(this);
39105         }
39106         this.panels.add(panel);
39107         el.setStyle("position", "absolute");
39108         if(!panel.background){
39109             this.setActivePanel(panel);
39110             if(this.config.initialSize && this.panels.getCount()==1){
39111                 this.resizeTo(this.config.initialSize);
39112             }
39113         }
39114         this.fireEvent("paneladded", this, panel);
39115         return panel;
39116     },
39117     
39118     /**
39119      * Returns true if the panel is in this region.
39120      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39121      * @return {Boolean}
39122      */
39123     hasPanel : function(panel){
39124         if(typeof panel == "object"){ // must be panel obj
39125             panel = panel.getId();
39126         }
39127         return this.getPanel(panel) ? true : false;
39128     },
39129     
39130     /**
39131      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39132      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39133      * @param {Boolean} preservePanel Overrides the config preservePanel option
39134      * @return {Roo.ContentPanel} The panel that was removed
39135      */
39136     remove : function(panel, preservePanel){
39137         panel = this.getPanel(panel);
39138         if(!panel){
39139             return null;
39140         }
39141         var e = {};
39142         this.fireEvent("beforeremove", this, panel, e);
39143         if(e.cancel === true){
39144             return null;
39145         }
39146         var panelId = panel.getId();
39147         this.panels.removeKey(panelId);
39148         return panel;
39149     },
39150     
39151     /**
39152      * Returns the panel specified or null if it's not in this region.
39153      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39154      * @return {Roo.ContentPanel}
39155      */
39156     getPanel : function(id){
39157         if(typeof id == "object"){ // must be panel obj
39158             return id;
39159         }
39160         return this.panels.get(id);
39161     },
39162     
39163     /**
39164      * Returns this regions position (north/south/east/west/center).
39165      * @return {String} 
39166      */
39167     getPosition: function(){
39168         return this.position;    
39169     }
39170 });/*
39171  * Based on:
39172  * Ext JS Library 1.1.1
39173  * Copyright(c) 2006-2007, Ext JS, LLC.
39174  *
39175  * Originally Released Under LGPL - original licence link has changed is not relivant.
39176  *
39177  * Fork - LGPL
39178  * <script type="text/javascript">
39179  */
39180  
39181 /**
39182  * @class Roo.bootstrap.layout.Region
39183  * @extends Roo.bootstrap.layout.Basic
39184  * This class represents a region in a layout manager.
39185  
39186  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39187  * @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})
39188  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39189  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39190  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39191  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39192  * @cfg {String}    title           The title for the region (overrides panel titles)
39193  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39194  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39195  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39196  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39197  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39198  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39199  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39200  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39201  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39202  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39203
39204  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39205  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39206  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39207  * @cfg {Number}    width           For East/West panels
39208  * @cfg {Number}    height          For North/South panels
39209  * @cfg {Boolean}   split           To show the splitter
39210  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39211  * 
39212  * @cfg {string}   cls             Extra CSS classes to add to region
39213  * 
39214  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39215  * @cfg {string}   region  the region that it inhabits..
39216  *
39217
39218  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39219  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39220
39221  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39222  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39223  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39224  */
39225 Roo.bootstrap.layout.Region = function(config)
39226 {
39227     this.applyConfig(config);
39228
39229     var mgr = config.mgr;
39230     var pos = config.region;
39231     config.skipConfig = true;
39232     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39233     
39234     if (mgr.el) {
39235         this.onRender(mgr.el);   
39236     }
39237      
39238     this.visible = true;
39239     this.collapsed = false;
39240     this.unrendered_panels = [];
39241 };
39242
39243 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39244
39245     position: '', // set by wrapper (eg. north/south etc..)
39246     unrendered_panels : null,  // unrendered panels.
39247     
39248     tabPosition : false,
39249     
39250     mgr: false, // points to 'Border'
39251     
39252     
39253     createBody : function(){
39254         /** This region's body element 
39255         * @type Roo.Element */
39256         this.bodyEl = this.el.createChild({
39257                 tag: "div",
39258                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39259         });
39260     },
39261
39262     onRender: function(ctr, pos)
39263     {
39264         var dh = Roo.DomHelper;
39265         /** This region's container element 
39266         * @type Roo.Element */
39267         this.el = dh.append(ctr.dom, {
39268                 tag: "div",
39269                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39270             }, true);
39271         /** This region's title element 
39272         * @type Roo.Element */
39273     
39274         this.titleEl = dh.append(this.el.dom,  {
39275                 tag: "div",
39276                 unselectable: "on",
39277                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39278                 children:[
39279                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39280                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39281                 ]
39282             }, true);
39283         
39284         this.titleEl.enableDisplayMode();
39285         /** This region's title text element 
39286         * @type HTMLElement */
39287         this.titleTextEl = this.titleEl.dom.firstChild;
39288         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39289         /*
39290         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39291         this.closeBtn.enableDisplayMode();
39292         this.closeBtn.on("click", this.closeClicked, this);
39293         this.closeBtn.hide();
39294     */
39295         this.createBody(this.config);
39296         if(this.config.hideWhenEmpty){
39297             this.hide();
39298             this.on("paneladded", this.validateVisibility, this);
39299             this.on("panelremoved", this.validateVisibility, this);
39300         }
39301         if(this.autoScroll){
39302             this.bodyEl.setStyle("overflow", "auto");
39303         }else{
39304             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39305         }
39306         //if(c.titlebar !== false){
39307             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39308                 this.titleEl.hide();
39309             }else{
39310                 this.titleEl.show();
39311                 if(this.config.title){
39312                     this.titleTextEl.innerHTML = this.config.title;
39313                 }
39314             }
39315         //}
39316         if(this.config.collapsed){
39317             this.collapse(true);
39318         }
39319         if(this.config.hidden){
39320             this.hide();
39321         }
39322         
39323         if (this.unrendered_panels && this.unrendered_panels.length) {
39324             for (var i =0;i< this.unrendered_panels.length; i++) {
39325                 this.add(this.unrendered_panels[i]);
39326             }
39327             this.unrendered_panels = null;
39328             
39329         }
39330         
39331     },
39332     
39333     applyConfig : function(c)
39334     {
39335         /*
39336          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39337             var dh = Roo.DomHelper;
39338             if(c.titlebar !== false){
39339                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39340                 this.collapseBtn.on("click", this.collapse, this);
39341                 this.collapseBtn.enableDisplayMode();
39342                 /*
39343                 if(c.showPin === true || this.showPin){
39344                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39345                     this.stickBtn.enableDisplayMode();
39346                     this.stickBtn.on("click", this.expand, this);
39347                     this.stickBtn.hide();
39348                 }
39349                 
39350             }
39351             */
39352             /** This region's collapsed element
39353             * @type Roo.Element */
39354             /*
39355              *
39356             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39357                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39358             ]}, true);
39359             
39360             if(c.floatable !== false){
39361                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39362                this.collapsedEl.on("click", this.collapseClick, this);
39363             }
39364
39365             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39366                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39367                    id: "message", unselectable: "on", style:{"float":"left"}});
39368                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39369              }
39370             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39371             this.expandBtn.on("click", this.expand, this);
39372             
39373         }
39374         
39375         if(this.collapseBtn){
39376             this.collapseBtn.setVisible(c.collapsible == true);
39377         }
39378         
39379         this.cmargins = c.cmargins || this.cmargins ||
39380                          (this.position == "west" || this.position == "east" ?
39381                              {top: 0, left: 2, right:2, bottom: 0} :
39382                              {top: 2, left: 0, right:0, bottom: 2});
39383         */
39384         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39385         
39386         
39387         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39388         
39389         this.autoScroll = c.autoScroll || false;
39390         
39391         
39392        
39393         
39394         this.duration = c.duration || .30;
39395         this.slideDuration = c.slideDuration || .45;
39396         this.config = c;
39397        
39398     },
39399     /**
39400      * Returns true if this region is currently visible.
39401      * @return {Boolean}
39402      */
39403     isVisible : function(){
39404         return this.visible;
39405     },
39406
39407     /**
39408      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39409      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39410      */
39411     //setCollapsedTitle : function(title){
39412     //    title = title || "&#160;";
39413      //   if(this.collapsedTitleTextEl){
39414       //      this.collapsedTitleTextEl.innerHTML = title;
39415        // }
39416     //},
39417
39418     getBox : function(){
39419         var b;
39420       //  if(!this.collapsed){
39421             b = this.el.getBox(false, true);
39422        // }else{
39423           //  b = this.collapsedEl.getBox(false, true);
39424         //}
39425         return b;
39426     },
39427
39428     getMargins : function(){
39429         return this.margins;
39430         //return this.collapsed ? this.cmargins : this.margins;
39431     },
39432 /*
39433     highlight : function(){
39434         this.el.addClass("x-layout-panel-dragover");
39435     },
39436
39437     unhighlight : function(){
39438         this.el.removeClass("x-layout-panel-dragover");
39439     },
39440 */
39441     updateBox : function(box)
39442     {
39443         if (!this.bodyEl) {
39444             return; // not rendered yet..
39445         }
39446         
39447         this.box = box;
39448         if(!this.collapsed){
39449             this.el.dom.style.left = box.x + "px";
39450             this.el.dom.style.top = box.y + "px";
39451             this.updateBody(box.width, box.height);
39452         }else{
39453             this.collapsedEl.dom.style.left = box.x + "px";
39454             this.collapsedEl.dom.style.top = box.y + "px";
39455             this.collapsedEl.setSize(box.width, box.height);
39456         }
39457         if(this.tabs){
39458             this.tabs.autoSizeTabs();
39459         }
39460     },
39461
39462     updateBody : function(w, h)
39463     {
39464         if(w !== null){
39465             this.el.setWidth(w);
39466             w -= this.el.getBorderWidth("rl");
39467             if(this.config.adjustments){
39468                 w += this.config.adjustments[0];
39469             }
39470         }
39471         if(h !== null && h > 0){
39472             this.el.setHeight(h);
39473             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39474             h -= this.el.getBorderWidth("tb");
39475             if(this.config.adjustments){
39476                 h += this.config.adjustments[1];
39477             }
39478             this.bodyEl.setHeight(h);
39479             if(this.tabs){
39480                 h = this.tabs.syncHeight(h);
39481             }
39482         }
39483         if(this.panelSize){
39484             w = w !== null ? w : this.panelSize.width;
39485             h = h !== null ? h : this.panelSize.height;
39486         }
39487         if(this.activePanel){
39488             var el = this.activePanel.getEl();
39489             w = w !== null ? w : el.getWidth();
39490             h = h !== null ? h : el.getHeight();
39491             this.panelSize = {width: w, height: h};
39492             this.activePanel.setSize(w, h);
39493         }
39494         if(Roo.isIE && this.tabs){
39495             this.tabs.el.repaint();
39496         }
39497     },
39498
39499     /**
39500      * Returns the container element for this region.
39501      * @return {Roo.Element}
39502      */
39503     getEl : function(){
39504         return this.el;
39505     },
39506
39507     /**
39508      * Hides this region.
39509      */
39510     hide : function(){
39511         //if(!this.collapsed){
39512             this.el.dom.style.left = "-2000px";
39513             this.el.hide();
39514         //}else{
39515          //   this.collapsedEl.dom.style.left = "-2000px";
39516          //   this.collapsedEl.hide();
39517        // }
39518         this.visible = false;
39519         this.fireEvent("visibilitychange", this, false);
39520     },
39521
39522     /**
39523      * Shows this region if it was previously hidden.
39524      */
39525     show : function(){
39526         //if(!this.collapsed){
39527             this.el.show();
39528         //}else{
39529         //    this.collapsedEl.show();
39530        // }
39531         this.visible = true;
39532         this.fireEvent("visibilitychange", this, true);
39533     },
39534 /*
39535     closeClicked : function(){
39536         if(this.activePanel){
39537             this.remove(this.activePanel);
39538         }
39539     },
39540
39541     collapseClick : function(e){
39542         if(this.isSlid){
39543            e.stopPropagation();
39544            this.slideIn();
39545         }else{
39546            e.stopPropagation();
39547            this.slideOut();
39548         }
39549     },
39550 */
39551     /**
39552      * Collapses this region.
39553      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39554      */
39555     /*
39556     collapse : function(skipAnim, skipCheck = false){
39557         if(this.collapsed) {
39558             return;
39559         }
39560         
39561         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39562             
39563             this.collapsed = true;
39564             if(this.split){
39565                 this.split.el.hide();
39566             }
39567             if(this.config.animate && skipAnim !== true){
39568                 this.fireEvent("invalidated", this);
39569                 this.animateCollapse();
39570             }else{
39571                 this.el.setLocation(-20000,-20000);
39572                 this.el.hide();
39573                 this.collapsedEl.show();
39574                 this.fireEvent("collapsed", this);
39575                 this.fireEvent("invalidated", this);
39576             }
39577         }
39578         
39579     },
39580 */
39581     animateCollapse : function(){
39582         // overridden
39583     },
39584
39585     /**
39586      * Expands this region if it was previously collapsed.
39587      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39588      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39589      */
39590     /*
39591     expand : function(e, skipAnim){
39592         if(e) {
39593             e.stopPropagation();
39594         }
39595         if(!this.collapsed || this.el.hasActiveFx()) {
39596             return;
39597         }
39598         if(this.isSlid){
39599             this.afterSlideIn();
39600             skipAnim = true;
39601         }
39602         this.collapsed = false;
39603         if(this.config.animate && skipAnim !== true){
39604             this.animateExpand();
39605         }else{
39606             this.el.show();
39607             if(this.split){
39608                 this.split.el.show();
39609             }
39610             this.collapsedEl.setLocation(-2000,-2000);
39611             this.collapsedEl.hide();
39612             this.fireEvent("invalidated", this);
39613             this.fireEvent("expanded", this);
39614         }
39615     },
39616 */
39617     animateExpand : function(){
39618         // overridden
39619     },
39620
39621     initTabs : function()
39622     {
39623         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39624         
39625         var ts = new Roo.bootstrap.panel.Tabs({
39626             el: this.bodyEl.dom,
39627             region : this,
39628             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39629             disableTooltips: this.config.disableTabTips,
39630             toolbar : this.config.toolbar
39631         });
39632         
39633         if(this.config.hideTabs){
39634             ts.stripWrap.setDisplayed(false);
39635         }
39636         this.tabs = ts;
39637         ts.resizeTabs = this.config.resizeTabs === true;
39638         ts.minTabWidth = this.config.minTabWidth || 40;
39639         ts.maxTabWidth = this.config.maxTabWidth || 250;
39640         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39641         ts.monitorResize = false;
39642         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39643         ts.bodyEl.addClass('roo-layout-tabs-body');
39644         this.panels.each(this.initPanelAsTab, this);
39645     },
39646
39647     initPanelAsTab : function(panel){
39648         var ti = this.tabs.addTab(
39649             panel.getEl().id,
39650             panel.getTitle(),
39651             null,
39652             this.config.closeOnTab && panel.isClosable(),
39653             panel.tpl
39654         );
39655         if(panel.tabTip !== undefined){
39656             ti.setTooltip(panel.tabTip);
39657         }
39658         ti.on("activate", function(){
39659               this.setActivePanel(panel);
39660         }, this);
39661         
39662         if(this.config.closeOnTab){
39663             ti.on("beforeclose", function(t, e){
39664                 e.cancel = true;
39665                 this.remove(panel);
39666             }, this);
39667         }
39668         
39669         panel.tabItem = ti;
39670         
39671         return ti;
39672     },
39673
39674     updatePanelTitle : function(panel, title)
39675     {
39676         if(this.activePanel == panel){
39677             this.updateTitle(title);
39678         }
39679         if(this.tabs){
39680             var ti = this.tabs.getTab(panel.getEl().id);
39681             ti.setText(title);
39682             if(panel.tabTip !== undefined){
39683                 ti.setTooltip(panel.tabTip);
39684             }
39685         }
39686     },
39687
39688     updateTitle : function(title){
39689         if(this.titleTextEl && !this.config.title){
39690             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39691         }
39692     },
39693
39694     setActivePanel : function(panel)
39695     {
39696         panel = this.getPanel(panel);
39697         if(this.activePanel && this.activePanel != panel){
39698             if(this.activePanel.setActiveState(false) === false){
39699                 return;
39700             }
39701         }
39702         this.activePanel = panel;
39703         panel.setActiveState(true);
39704         if(this.panelSize){
39705             panel.setSize(this.panelSize.width, this.panelSize.height);
39706         }
39707         if(this.closeBtn){
39708             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39709         }
39710         this.updateTitle(panel.getTitle());
39711         if(this.tabs){
39712             this.fireEvent("invalidated", this);
39713         }
39714         this.fireEvent("panelactivated", this, panel);
39715     },
39716
39717     /**
39718      * Shows the specified panel.
39719      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39720      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39721      */
39722     showPanel : function(panel)
39723     {
39724         panel = this.getPanel(panel);
39725         if(panel){
39726             if(this.tabs){
39727                 var tab = this.tabs.getTab(panel.getEl().id);
39728                 if(tab.isHidden()){
39729                     this.tabs.unhideTab(tab.id);
39730                 }
39731                 tab.activate();
39732             }else{
39733                 this.setActivePanel(panel);
39734             }
39735         }
39736         return panel;
39737     },
39738
39739     /**
39740      * Get the active panel for this region.
39741      * @return {Roo.ContentPanel} The active panel or null
39742      */
39743     getActivePanel : function(){
39744         return this.activePanel;
39745     },
39746
39747     validateVisibility : function(){
39748         if(this.panels.getCount() < 1){
39749             this.updateTitle("&#160;");
39750             this.closeBtn.hide();
39751             this.hide();
39752         }else{
39753             if(!this.isVisible()){
39754                 this.show();
39755             }
39756         }
39757     },
39758
39759     /**
39760      * Adds the passed ContentPanel(s) to this region.
39761      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39762      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39763      */
39764     add : function(panel)
39765     {
39766         if(arguments.length > 1){
39767             for(var i = 0, len = arguments.length; i < len; i++) {
39768                 this.add(arguments[i]);
39769             }
39770             return null;
39771         }
39772         
39773         // if we have not been rendered yet, then we can not really do much of this..
39774         if (!this.bodyEl) {
39775             this.unrendered_panels.push(panel);
39776             return panel;
39777         }
39778         
39779         
39780         
39781         
39782         if(this.hasPanel(panel)){
39783             this.showPanel(panel);
39784             return panel;
39785         }
39786         panel.setRegion(this);
39787         this.panels.add(panel);
39788        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39789             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39790             // and hide them... ???
39791             this.bodyEl.dom.appendChild(panel.getEl().dom);
39792             if(panel.background !== true){
39793                 this.setActivePanel(panel);
39794             }
39795             this.fireEvent("paneladded", this, panel);
39796             return panel;
39797         }
39798         */
39799         if(!this.tabs){
39800             this.initTabs();
39801         }else{
39802             this.initPanelAsTab(panel);
39803         }
39804         
39805         
39806         if(panel.background !== true){
39807             this.tabs.activate(panel.getEl().id);
39808         }
39809         this.fireEvent("paneladded", this, panel);
39810         return panel;
39811     },
39812
39813     /**
39814      * Hides the tab for the specified panel.
39815      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39816      */
39817     hidePanel : function(panel){
39818         if(this.tabs && (panel = this.getPanel(panel))){
39819             this.tabs.hideTab(panel.getEl().id);
39820         }
39821     },
39822
39823     /**
39824      * Unhides the tab for a previously hidden panel.
39825      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39826      */
39827     unhidePanel : function(panel){
39828         if(this.tabs && (panel = this.getPanel(panel))){
39829             this.tabs.unhideTab(panel.getEl().id);
39830         }
39831     },
39832
39833     clearPanels : function(){
39834         while(this.panels.getCount() > 0){
39835              this.remove(this.panels.first());
39836         }
39837     },
39838
39839     /**
39840      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39841      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39842      * @param {Boolean} preservePanel Overrides the config preservePanel option
39843      * @return {Roo.ContentPanel} The panel that was removed
39844      */
39845     remove : function(panel, preservePanel)
39846     {
39847         panel = this.getPanel(panel);
39848         if(!panel){
39849             return null;
39850         }
39851         var e = {};
39852         this.fireEvent("beforeremove", this, panel, e);
39853         if(e.cancel === true){
39854             return null;
39855         }
39856         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39857         var panelId = panel.getId();
39858         this.panels.removeKey(panelId);
39859         if(preservePanel){
39860             document.body.appendChild(panel.getEl().dom);
39861         }
39862         if(this.tabs){
39863             this.tabs.removeTab(panel.getEl().id);
39864         }else if (!preservePanel){
39865             this.bodyEl.dom.removeChild(panel.getEl().dom);
39866         }
39867         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39868             var p = this.panels.first();
39869             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39870             tempEl.appendChild(p.getEl().dom);
39871             this.bodyEl.update("");
39872             this.bodyEl.dom.appendChild(p.getEl().dom);
39873             tempEl = null;
39874             this.updateTitle(p.getTitle());
39875             this.tabs = null;
39876             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39877             this.setActivePanel(p);
39878         }
39879         panel.setRegion(null);
39880         if(this.activePanel == panel){
39881             this.activePanel = null;
39882         }
39883         if(this.config.autoDestroy !== false && preservePanel !== true){
39884             try{panel.destroy();}catch(e){}
39885         }
39886         this.fireEvent("panelremoved", this, panel);
39887         return panel;
39888     },
39889
39890     /**
39891      * Returns the TabPanel component used by this region
39892      * @return {Roo.TabPanel}
39893      */
39894     getTabs : function(){
39895         return this.tabs;
39896     },
39897
39898     createTool : function(parentEl, className){
39899         var btn = Roo.DomHelper.append(parentEl, {
39900             tag: "div",
39901             cls: "x-layout-tools-button",
39902             children: [ {
39903                 tag: "div",
39904                 cls: "roo-layout-tools-button-inner " + className,
39905                 html: "&#160;"
39906             }]
39907         }, true);
39908         btn.addClassOnOver("roo-layout-tools-button-over");
39909         return btn;
39910     }
39911 });/*
39912  * Based on:
39913  * Ext JS Library 1.1.1
39914  * Copyright(c) 2006-2007, Ext JS, LLC.
39915  *
39916  * Originally Released Under LGPL - original licence link has changed is not relivant.
39917  *
39918  * Fork - LGPL
39919  * <script type="text/javascript">
39920  */
39921  
39922
39923
39924 /**
39925  * @class Roo.SplitLayoutRegion
39926  * @extends Roo.LayoutRegion
39927  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39928  */
39929 Roo.bootstrap.layout.Split = function(config){
39930     this.cursor = config.cursor;
39931     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39932 };
39933
39934 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39935 {
39936     splitTip : "Drag to resize.",
39937     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39938     useSplitTips : false,
39939
39940     applyConfig : function(config){
39941         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39942     },
39943     
39944     onRender : function(ctr,pos) {
39945         
39946         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39947         if(!this.config.split){
39948             return;
39949         }
39950         if(!this.split){
39951             
39952             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39953                             tag: "div",
39954                             id: this.el.id + "-split",
39955                             cls: "roo-layout-split roo-layout-split-"+this.position,
39956                             html: "&#160;"
39957             });
39958             /** The SplitBar for this region 
39959             * @type Roo.SplitBar */
39960             // does not exist yet...
39961             Roo.log([this.position, this.orientation]);
39962             
39963             this.split = new Roo.bootstrap.SplitBar({
39964                 dragElement : splitEl,
39965                 resizingElement: this.el,
39966                 orientation : this.orientation
39967             });
39968             
39969             this.split.on("moved", this.onSplitMove, this);
39970             this.split.useShim = this.config.useShim === true;
39971             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39972             if(this.useSplitTips){
39973                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39974             }
39975             //if(config.collapsible){
39976             //    this.split.el.on("dblclick", this.collapse,  this);
39977             //}
39978         }
39979         if(typeof this.config.minSize != "undefined"){
39980             this.split.minSize = this.config.minSize;
39981         }
39982         if(typeof this.config.maxSize != "undefined"){
39983             this.split.maxSize = this.config.maxSize;
39984         }
39985         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39986             this.hideSplitter();
39987         }
39988         
39989     },
39990
39991     getHMaxSize : function(){
39992          var cmax = this.config.maxSize || 10000;
39993          var center = this.mgr.getRegion("center");
39994          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39995     },
39996
39997     getVMaxSize : function(){
39998          var cmax = this.config.maxSize || 10000;
39999          var center = this.mgr.getRegion("center");
40000          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
40001     },
40002
40003     onSplitMove : function(split, newSize){
40004         this.fireEvent("resized", this, newSize);
40005     },
40006     
40007     /** 
40008      * Returns the {@link Roo.SplitBar} for this region.
40009      * @return {Roo.SplitBar}
40010      */
40011     getSplitBar : function(){
40012         return this.split;
40013     },
40014     
40015     hide : function(){
40016         this.hideSplitter();
40017         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40018     },
40019
40020     hideSplitter : function(){
40021         if(this.split){
40022             this.split.el.setLocation(-2000,-2000);
40023             this.split.el.hide();
40024         }
40025     },
40026
40027     show : function(){
40028         if(this.split){
40029             this.split.el.show();
40030         }
40031         Roo.bootstrap.layout.Split.superclass.show.call(this);
40032     },
40033     
40034     beforeSlide: function(){
40035         if(Roo.isGecko){// firefox overflow auto bug workaround
40036             this.bodyEl.clip();
40037             if(this.tabs) {
40038                 this.tabs.bodyEl.clip();
40039             }
40040             if(this.activePanel){
40041                 this.activePanel.getEl().clip();
40042                 
40043                 if(this.activePanel.beforeSlide){
40044                     this.activePanel.beforeSlide();
40045                 }
40046             }
40047         }
40048     },
40049     
40050     afterSlide : function(){
40051         if(Roo.isGecko){// firefox overflow auto bug workaround
40052             this.bodyEl.unclip();
40053             if(this.tabs) {
40054                 this.tabs.bodyEl.unclip();
40055             }
40056             if(this.activePanel){
40057                 this.activePanel.getEl().unclip();
40058                 if(this.activePanel.afterSlide){
40059                     this.activePanel.afterSlide();
40060                 }
40061             }
40062         }
40063     },
40064
40065     initAutoHide : function(){
40066         if(this.autoHide !== false){
40067             if(!this.autoHideHd){
40068                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40069                 this.autoHideHd = {
40070                     "mouseout": function(e){
40071                         if(!e.within(this.el, true)){
40072                             st.delay(500);
40073                         }
40074                     },
40075                     "mouseover" : function(e){
40076                         st.cancel();
40077                     },
40078                     scope : this
40079                 };
40080             }
40081             this.el.on(this.autoHideHd);
40082         }
40083     },
40084
40085     clearAutoHide : function(){
40086         if(this.autoHide !== false){
40087             this.el.un("mouseout", this.autoHideHd.mouseout);
40088             this.el.un("mouseover", this.autoHideHd.mouseover);
40089         }
40090     },
40091
40092     clearMonitor : function(){
40093         Roo.get(document).un("click", this.slideInIf, this);
40094     },
40095
40096     // these names are backwards but not changed for compat
40097     slideOut : function(){
40098         if(this.isSlid || this.el.hasActiveFx()){
40099             return;
40100         }
40101         this.isSlid = true;
40102         if(this.collapseBtn){
40103             this.collapseBtn.hide();
40104         }
40105         this.closeBtnState = this.closeBtn.getStyle('display');
40106         this.closeBtn.hide();
40107         if(this.stickBtn){
40108             this.stickBtn.show();
40109         }
40110         this.el.show();
40111         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40112         this.beforeSlide();
40113         this.el.setStyle("z-index", 10001);
40114         this.el.slideIn(this.getSlideAnchor(), {
40115             callback: function(){
40116                 this.afterSlide();
40117                 this.initAutoHide();
40118                 Roo.get(document).on("click", this.slideInIf, this);
40119                 this.fireEvent("slideshow", this);
40120             },
40121             scope: this,
40122             block: true
40123         });
40124     },
40125
40126     afterSlideIn : function(){
40127         this.clearAutoHide();
40128         this.isSlid = false;
40129         this.clearMonitor();
40130         this.el.setStyle("z-index", "");
40131         if(this.collapseBtn){
40132             this.collapseBtn.show();
40133         }
40134         this.closeBtn.setStyle('display', this.closeBtnState);
40135         if(this.stickBtn){
40136             this.stickBtn.hide();
40137         }
40138         this.fireEvent("slidehide", this);
40139     },
40140
40141     slideIn : function(cb){
40142         if(!this.isSlid || this.el.hasActiveFx()){
40143             Roo.callback(cb);
40144             return;
40145         }
40146         this.isSlid = false;
40147         this.beforeSlide();
40148         this.el.slideOut(this.getSlideAnchor(), {
40149             callback: function(){
40150                 this.el.setLeftTop(-10000, -10000);
40151                 this.afterSlide();
40152                 this.afterSlideIn();
40153                 Roo.callback(cb);
40154             },
40155             scope: this,
40156             block: true
40157         });
40158     },
40159     
40160     slideInIf : function(e){
40161         if(!e.within(this.el)){
40162             this.slideIn();
40163         }
40164     },
40165
40166     animateCollapse : function(){
40167         this.beforeSlide();
40168         this.el.setStyle("z-index", 20000);
40169         var anchor = this.getSlideAnchor();
40170         this.el.slideOut(anchor, {
40171             callback : function(){
40172                 this.el.setStyle("z-index", "");
40173                 this.collapsedEl.slideIn(anchor, {duration:.3});
40174                 this.afterSlide();
40175                 this.el.setLocation(-10000,-10000);
40176                 this.el.hide();
40177                 this.fireEvent("collapsed", this);
40178             },
40179             scope: this,
40180             block: true
40181         });
40182     },
40183
40184     animateExpand : function(){
40185         this.beforeSlide();
40186         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40187         this.el.setStyle("z-index", 20000);
40188         this.collapsedEl.hide({
40189             duration:.1
40190         });
40191         this.el.slideIn(this.getSlideAnchor(), {
40192             callback : function(){
40193                 this.el.setStyle("z-index", "");
40194                 this.afterSlide();
40195                 if(this.split){
40196                     this.split.el.show();
40197                 }
40198                 this.fireEvent("invalidated", this);
40199                 this.fireEvent("expanded", this);
40200             },
40201             scope: this,
40202             block: true
40203         });
40204     },
40205
40206     anchors : {
40207         "west" : "left",
40208         "east" : "right",
40209         "north" : "top",
40210         "south" : "bottom"
40211     },
40212
40213     sanchors : {
40214         "west" : "l",
40215         "east" : "r",
40216         "north" : "t",
40217         "south" : "b"
40218     },
40219
40220     canchors : {
40221         "west" : "tl-tr",
40222         "east" : "tr-tl",
40223         "north" : "tl-bl",
40224         "south" : "bl-tl"
40225     },
40226
40227     getAnchor : function(){
40228         return this.anchors[this.position];
40229     },
40230
40231     getCollapseAnchor : function(){
40232         return this.canchors[this.position];
40233     },
40234
40235     getSlideAnchor : function(){
40236         return this.sanchors[this.position];
40237     },
40238
40239     getAlignAdj : function(){
40240         var cm = this.cmargins;
40241         switch(this.position){
40242             case "west":
40243                 return [0, 0];
40244             break;
40245             case "east":
40246                 return [0, 0];
40247             break;
40248             case "north":
40249                 return [0, 0];
40250             break;
40251             case "south":
40252                 return [0, 0];
40253             break;
40254         }
40255     },
40256
40257     getExpandAdj : function(){
40258         var c = this.collapsedEl, cm = this.cmargins;
40259         switch(this.position){
40260             case "west":
40261                 return [-(cm.right+c.getWidth()+cm.left), 0];
40262             break;
40263             case "east":
40264                 return [cm.right+c.getWidth()+cm.left, 0];
40265             break;
40266             case "north":
40267                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40268             break;
40269             case "south":
40270                 return [0, cm.top+cm.bottom+c.getHeight()];
40271             break;
40272         }
40273     }
40274 });/*
40275  * Based on:
40276  * Ext JS Library 1.1.1
40277  * Copyright(c) 2006-2007, Ext JS, LLC.
40278  *
40279  * Originally Released Under LGPL - original licence link has changed is not relivant.
40280  *
40281  * Fork - LGPL
40282  * <script type="text/javascript">
40283  */
40284 /*
40285  * These classes are private internal classes
40286  */
40287 Roo.bootstrap.layout.Center = function(config){
40288     config.region = "center";
40289     Roo.bootstrap.layout.Region.call(this, config);
40290     this.visible = true;
40291     this.minWidth = config.minWidth || 20;
40292     this.minHeight = config.minHeight || 20;
40293 };
40294
40295 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40296     hide : function(){
40297         // center panel can't be hidden
40298     },
40299     
40300     show : function(){
40301         // center panel can't be hidden
40302     },
40303     
40304     getMinWidth: function(){
40305         return this.minWidth;
40306     },
40307     
40308     getMinHeight: function(){
40309         return this.minHeight;
40310     }
40311 });
40312
40313
40314
40315
40316  
40317
40318
40319
40320
40321
40322
40323 Roo.bootstrap.layout.North = function(config)
40324 {
40325     config.region = 'north';
40326     config.cursor = 'n-resize';
40327     
40328     Roo.bootstrap.layout.Split.call(this, config);
40329     
40330     
40331     if(this.split){
40332         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40333         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40334         this.split.el.addClass("roo-layout-split-v");
40335     }
40336     //var size = config.initialSize || config.height;
40337     //if(this.el && typeof size != "undefined"){
40338     //    this.el.setHeight(size);
40339     //}
40340 };
40341 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40342 {
40343     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40344      
40345      
40346     onRender : function(ctr, pos)
40347     {
40348         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40349         var size = this.config.initialSize || this.config.height;
40350         if(this.el && typeof size != "undefined"){
40351             this.el.setHeight(size);
40352         }
40353     
40354     },
40355     
40356     getBox : function(){
40357         if(this.collapsed){
40358             return this.collapsedEl.getBox();
40359         }
40360         var box = this.el.getBox();
40361         if(this.split){
40362             box.height += this.split.el.getHeight();
40363         }
40364         return box;
40365     },
40366     
40367     updateBox : function(box){
40368         if(this.split && !this.collapsed){
40369             box.height -= this.split.el.getHeight();
40370             this.split.el.setLeft(box.x);
40371             this.split.el.setTop(box.y+box.height);
40372             this.split.el.setWidth(box.width);
40373         }
40374         if(this.collapsed){
40375             this.updateBody(box.width, null);
40376         }
40377         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40378     }
40379 });
40380
40381
40382
40383
40384
40385 Roo.bootstrap.layout.South = function(config){
40386     config.region = 'south';
40387     config.cursor = 's-resize';
40388     Roo.bootstrap.layout.Split.call(this, config);
40389     if(this.split){
40390         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40391         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40392         this.split.el.addClass("roo-layout-split-v");
40393     }
40394     
40395 };
40396
40397 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40398     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40399     
40400     onRender : function(ctr, pos)
40401     {
40402         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40403         var size = this.config.initialSize || this.config.height;
40404         if(this.el && typeof size != "undefined"){
40405             this.el.setHeight(size);
40406         }
40407     
40408     },
40409     
40410     getBox : function(){
40411         if(this.collapsed){
40412             return this.collapsedEl.getBox();
40413         }
40414         var box = this.el.getBox();
40415         if(this.split){
40416             var sh = this.split.el.getHeight();
40417             box.height += sh;
40418             box.y -= sh;
40419         }
40420         return box;
40421     },
40422     
40423     updateBox : function(box){
40424         if(this.split && !this.collapsed){
40425             var sh = this.split.el.getHeight();
40426             box.height -= sh;
40427             box.y += sh;
40428             this.split.el.setLeft(box.x);
40429             this.split.el.setTop(box.y-sh);
40430             this.split.el.setWidth(box.width);
40431         }
40432         if(this.collapsed){
40433             this.updateBody(box.width, null);
40434         }
40435         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40436     }
40437 });
40438
40439 Roo.bootstrap.layout.East = function(config){
40440     config.region = "east";
40441     config.cursor = "e-resize";
40442     Roo.bootstrap.layout.Split.call(this, config);
40443     if(this.split){
40444         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40445         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40446         this.split.el.addClass("roo-layout-split-h");
40447     }
40448     
40449 };
40450 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40451     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40452     
40453     onRender : function(ctr, pos)
40454     {
40455         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40456         var size = this.config.initialSize || this.config.width;
40457         if(this.el && typeof size != "undefined"){
40458             this.el.setWidth(size);
40459         }
40460     
40461     },
40462     
40463     getBox : function(){
40464         if(this.collapsed){
40465             return this.collapsedEl.getBox();
40466         }
40467         var box = this.el.getBox();
40468         if(this.split){
40469             var sw = this.split.el.getWidth();
40470             box.width += sw;
40471             box.x -= sw;
40472         }
40473         return box;
40474     },
40475
40476     updateBox : function(box){
40477         if(this.split && !this.collapsed){
40478             var sw = this.split.el.getWidth();
40479             box.width -= sw;
40480             this.split.el.setLeft(box.x);
40481             this.split.el.setTop(box.y);
40482             this.split.el.setHeight(box.height);
40483             box.x += sw;
40484         }
40485         if(this.collapsed){
40486             this.updateBody(null, box.height);
40487         }
40488         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40489     }
40490 });
40491
40492 Roo.bootstrap.layout.West = function(config){
40493     config.region = "west";
40494     config.cursor = "w-resize";
40495     
40496     Roo.bootstrap.layout.Split.call(this, config);
40497     if(this.split){
40498         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40499         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40500         this.split.el.addClass("roo-layout-split-h");
40501     }
40502     
40503 };
40504 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40505     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40506     
40507     onRender: function(ctr, pos)
40508     {
40509         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40510         var size = this.config.initialSize || this.config.width;
40511         if(typeof size != "undefined"){
40512             this.el.setWidth(size);
40513         }
40514     },
40515     
40516     getBox : function(){
40517         if(this.collapsed){
40518             return this.collapsedEl.getBox();
40519         }
40520         var box = this.el.getBox();
40521         if (box.width == 0) {
40522             box.width = this.config.width; // kludge?
40523         }
40524         if(this.split){
40525             box.width += this.split.el.getWidth();
40526         }
40527         return box;
40528     },
40529     
40530     updateBox : function(box){
40531         if(this.split && !this.collapsed){
40532             var sw = this.split.el.getWidth();
40533             box.width -= sw;
40534             this.split.el.setLeft(box.x+box.width);
40535             this.split.el.setTop(box.y);
40536             this.split.el.setHeight(box.height);
40537         }
40538         if(this.collapsed){
40539             this.updateBody(null, box.height);
40540         }
40541         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40542     }
40543 });Roo.namespace("Roo.bootstrap.panel");/*
40544  * Based on:
40545  * Ext JS Library 1.1.1
40546  * Copyright(c) 2006-2007, Ext JS, LLC.
40547  *
40548  * Originally Released Under LGPL - original licence link has changed is not relivant.
40549  *
40550  * Fork - LGPL
40551  * <script type="text/javascript">
40552  */
40553 /**
40554  * @class Roo.ContentPanel
40555  * @extends Roo.util.Observable
40556  * @builder-top
40557  * A basic ContentPanel element.
40558  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40559  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40560  * @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
40561  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40562  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40563  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40564  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40565  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40566  * @cfg {String} title          The title for this panel
40567  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40568  * @cfg {String} url            Calls {@link #setUrl} with this value
40569  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40570  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40571  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40572  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40573  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40574  * @cfg {Boolean} badges render the badges
40575  * @cfg {String} cls  extra classes to use  
40576  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40577
40578  * @constructor
40579  * Create a new ContentPanel.
40580  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40581  * @param {String/Object} config A string to set only the title or a config object
40582  * @param {String} content (optional) Set the HTML content for this panel
40583  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40584  */
40585 Roo.bootstrap.panel.Content = function( config){
40586     
40587     this.tpl = config.tpl || false;
40588     
40589     var el = config.el;
40590     var content = config.content;
40591
40592     if(config.autoCreate){ // xtype is available if this is called from factory
40593         el = Roo.id();
40594     }
40595     this.el = Roo.get(el);
40596     if(!this.el && config && config.autoCreate){
40597         if(typeof config.autoCreate == "object"){
40598             if(!config.autoCreate.id){
40599                 config.autoCreate.id = config.id||el;
40600             }
40601             this.el = Roo.DomHelper.append(document.body,
40602                         config.autoCreate, true);
40603         }else{
40604             var elcfg =  {
40605                 tag: "div",
40606                 cls: (config.cls || '') +
40607                     (config.background ? ' bg-' + config.background : '') +
40608                     " roo-layout-inactive-content",
40609                 id: config.id||el
40610             };
40611             if (config.iframe) {
40612                 elcfg.cn = [
40613                     {
40614                         tag : 'iframe',
40615                         style : 'border: 0px',
40616                         src : 'about:blank'
40617                     }
40618                 ];
40619             }
40620               
40621             if (config.html) {
40622                 elcfg.html = config.html;
40623                 
40624             }
40625                         
40626             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40627             if (config.iframe) {
40628                 this.iframeEl = this.el.select('iframe',true).first();
40629             }
40630             
40631         }
40632     } 
40633     this.closable = false;
40634     this.loaded = false;
40635     this.active = false;
40636    
40637       
40638     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40639         
40640         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40641         
40642         this.wrapEl = this.el; //this.el.wrap();
40643         var ti = [];
40644         if (config.toolbar.items) {
40645             ti = config.toolbar.items ;
40646             delete config.toolbar.items ;
40647         }
40648         
40649         var nitems = [];
40650         this.toolbar.render(this.wrapEl, 'before');
40651         for(var i =0;i < ti.length;i++) {
40652           //  Roo.log(['add child', items[i]]);
40653             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40654         }
40655         this.toolbar.items = nitems;
40656         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40657         delete config.toolbar;
40658         
40659     }
40660     /*
40661     // xtype created footer. - not sure if will work as we normally have to render first..
40662     if (this.footer && !this.footer.el && this.footer.xtype) {
40663         if (!this.wrapEl) {
40664             this.wrapEl = this.el.wrap();
40665         }
40666     
40667         this.footer.container = this.wrapEl.createChild();
40668          
40669         this.footer = Roo.factory(this.footer, Roo);
40670         
40671     }
40672     */
40673     
40674      if(typeof config == "string"){
40675         this.title = config;
40676     }else{
40677         Roo.apply(this, config);
40678     }
40679     
40680     if(this.resizeEl){
40681         this.resizeEl = Roo.get(this.resizeEl, true);
40682     }else{
40683         this.resizeEl = this.el;
40684     }
40685     // handle view.xtype
40686     
40687  
40688     
40689     
40690     this.addEvents({
40691         /**
40692          * @event activate
40693          * Fires when this panel is activated. 
40694          * @param {Roo.ContentPanel} this
40695          */
40696         "activate" : true,
40697         /**
40698          * @event deactivate
40699          * Fires when this panel is activated. 
40700          * @param {Roo.ContentPanel} this
40701          */
40702         "deactivate" : true,
40703
40704         /**
40705          * @event resize
40706          * Fires when this panel is resized if fitToFrame is true.
40707          * @param {Roo.ContentPanel} this
40708          * @param {Number} width The width after any component adjustments
40709          * @param {Number} height The height after any component adjustments
40710          */
40711         "resize" : true,
40712         
40713          /**
40714          * @event render
40715          * Fires when this tab is created
40716          * @param {Roo.ContentPanel} this
40717          */
40718         "render" : true,
40719         
40720           /**
40721          * @event scroll
40722          * Fires when this content is scrolled
40723          * @param {Roo.ContentPanel} this
40724          * @param {Event} scrollEvent
40725          */
40726         "scroll" : true
40727         
40728         
40729         
40730     });
40731     
40732
40733     
40734     
40735     if(this.autoScroll && !this.iframe){
40736         this.resizeEl.setStyle("overflow", "auto");
40737         this.resizeEl.on('scroll', this.onScroll, this);
40738     } else {
40739         // fix randome scrolling
40740         //this.el.on('scroll', function() {
40741         //    Roo.log('fix random scolling');
40742         //    this.scrollTo('top',0); 
40743         //});
40744     }
40745     content = content || this.content;
40746     if(content){
40747         this.setContent(content);
40748     }
40749     if(config && config.url){
40750         this.setUrl(this.url, this.params, this.loadOnce);
40751     }
40752     
40753     
40754     
40755     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40756     
40757     if (this.view && typeof(this.view.xtype) != 'undefined') {
40758         this.view.el = this.el.appendChild(document.createElement("div"));
40759         this.view = Roo.factory(this.view); 
40760         this.view.render  &&  this.view.render(false, '');  
40761     }
40762     
40763     
40764     this.fireEvent('render', this);
40765 };
40766
40767 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40768     
40769     cls : '',
40770     background : '',
40771     
40772     tabTip : '',
40773     
40774     iframe : false,
40775     iframeEl : false,
40776     
40777     /* Resize Element - use this to work out scroll etc. */
40778     resizeEl : false,
40779     
40780     setRegion : function(region){
40781         this.region = region;
40782         this.setActiveClass(region && !this.background);
40783     },
40784     
40785     
40786     setActiveClass: function(state)
40787     {
40788         if(state){
40789            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40790            this.el.setStyle('position','relative');
40791         }else{
40792            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40793            this.el.setStyle('position', 'absolute');
40794         } 
40795     },
40796     
40797     /**
40798      * Returns the toolbar for this Panel if one was configured. 
40799      * @return {Roo.Toolbar} 
40800      */
40801     getToolbar : function(){
40802         return this.toolbar;
40803     },
40804     
40805     setActiveState : function(active)
40806     {
40807         this.active = active;
40808         this.setActiveClass(active);
40809         if(!active){
40810             if(this.fireEvent("deactivate", this) === false){
40811                 return false;
40812             }
40813             return true;
40814         }
40815         this.fireEvent("activate", this);
40816         return true;
40817     },
40818     /**
40819      * Updates this panel's element (not for iframe)
40820      * @param {String} content The new content
40821      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40822     */
40823     setContent : function(content, loadScripts){
40824         if (this.iframe) {
40825             return;
40826         }
40827         
40828         this.el.update(content, loadScripts);
40829     },
40830
40831     ignoreResize : function(w, h){
40832         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40833             return true;
40834         }else{
40835             this.lastSize = {width: w, height: h};
40836             return false;
40837         }
40838     },
40839     /**
40840      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40841      * @return {Roo.UpdateManager} The UpdateManager
40842      */
40843     getUpdateManager : function(){
40844         if (this.iframe) {
40845             return false;
40846         }
40847         return this.el.getUpdateManager();
40848     },
40849      /**
40850      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40851      * Does not work with IFRAME contents
40852      * @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:
40853 <pre><code>
40854 panel.load({
40855     url: "your-url.php",
40856     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40857     callback: yourFunction,
40858     scope: yourObject, //(optional scope)
40859     discardUrl: false,
40860     nocache: false,
40861     text: "Loading...",
40862     timeout: 30,
40863     scripts: false
40864 });
40865 </code></pre>
40866      
40867      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40868      * 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.
40869      * @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}
40870      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40871      * @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.
40872      * @return {Roo.ContentPanel} this
40873      */
40874     load : function(){
40875         
40876         if (this.iframe) {
40877             return this;
40878         }
40879         
40880         var um = this.el.getUpdateManager();
40881         um.update.apply(um, arguments);
40882         return this;
40883     },
40884
40885
40886     /**
40887      * 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.
40888      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40889      * @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)
40890      * @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)
40891      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40892      */
40893     setUrl : function(url, params, loadOnce){
40894         if (this.iframe) {
40895             this.iframeEl.dom.src = url;
40896             return false;
40897         }
40898         
40899         if(this.refreshDelegate){
40900             this.removeListener("activate", this.refreshDelegate);
40901         }
40902         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40903         this.on("activate", this.refreshDelegate);
40904         return this.el.getUpdateManager();
40905     },
40906     
40907     _handleRefresh : function(url, params, loadOnce){
40908         if(!loadOnce || !this.loaded){
40909             var updater = this.el.getUpdateManager();
40910             updater.update(url, params, this._setLoaded.createDelegate(this));
40911         }
40912     },
40913     
40914     _setLoaded : function(){
40915         this.loaded = true;
40916     }, 
40917     
40918     /**
40919      * Returns this panel's id
40920      * @return {String} 
40921      */
40922     getId : function(){
40923         return this.el.id;
40924     },
40925     
40926     /** 
40927      * Returns this panel's element - used by regiosn to add.
40928      * @return {Roo.Element} 
40929      */
40930     getEl : function(){
40931         return this.wrapEl || this.el;
40932     },
40933     
40934    
40935     
40936     adjustForComponents : function(width, height)
40937     {
40938         //Roo.log('adjustForComponents ');
40939         if(this.resizeEl != this.el){
40940             width -= this.el.getFrameWidth('lr');
40941             height -= this.el.getFrameWidth('tb');
40942         }
40943         if(this.toolbar){
40944             var te = this.toolbar.getEl();
40945             te.setWidth(width);
40946             height -= te.getHeight();
40947         }
40948         if(this.footer){
40949             var te = this.footer.getEl();
40950             te.setWidth(width);
40951             height -= te.getHeight();
40952         }
40953         
40954         
40955         if(this.adjustments){
40956             width += this.adjustments[0];
40957             height += this.adjustments[1];
40958         }
40959         return {"width": width, "height": height};
40960     },
40961     
40962     setSize : function(width, height){
40963         if(this.fitToFrame && !this.ignoreResize(width, height)){
40964             if(this.fitContainer && this.resizeEl != this.el){
40965                 this.el.setSize(width, height);
40966             }
40967             var size = this.adjustForComponents(width, height);
40968             if (this.iframe) {
40969                 this.iframeEl.setSize(width,height);
40970             }
40971             
40972             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40973             this.fireEvent('resize', this, size.width, size.height);
40974             
40975             
40976         }
40977     },
40978     
40979     /**
40980      * Returns this panel's title
40981      * @return {String} 
40982      */
40983     getTitle : function(){
40984         
40985         if (typeof(this.title) != 'object') {
40986             return this.title;
40987         }
40988         
40989         var t = '';
40990         for (var k in this.title) {
40991             if (!this.title.hasOwnProperty(k)) {
40992                 continue;
40993             }
40994             
40995             if (k.indexOf('-') >= 0) {
40996                 var s = k.split('-');
40997                 for (var i = 0; i<s.length; i++) {
40998                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40999                 }
41000             } else {
41001                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
41002             }
41003         }
41004         return t;
41005     },
41006     
41007     /**
41008      * Set this panel's title
41009      * @param {String} title
41010      */
41011     setTitle : function(title){
41012         this.title = title;
41013         if(this.region){
41014             this.region.updatePanelTitle(this, title);
41015         }
41016     },
41017     
41018     /**
41019      * Returns true is this panel was configured to be closable
41020      * @return {Boolean} 
41021      */
41022     isClosable : function(){
41023         return this.closable;
41024     },
41025     
41026     beforeSlide : function(){
41027         this.el.clip();
41028         this.resizeEl.clip();
41029     },
41030     
41031     afterSlide : function(){
41032         this.el.unclip();
41033         this.resizeEl.unclip();
41034     },
41035     
41036     /**
41037      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41038      *   Will fail silently if the {@link #setUrl} method has not been called.
41039      *   This does not activate the panel, just updates its content.
41040      */
41041     refresh : function(){
41042         if(this.refreshDelegate){
41043            this.loaded = false;
41044            this.refreshDelegate();
41045         }
41046     },
41047     
41048     /**
41049      * Destroys this panel
41050      */
41051     destroy : function(){
41052         this.el.removeAllListeners();
41053         var tempEl = document.createElement("span");
41054         tempEl.appendChild(this.el.dom);
41055         tempEl.innerHTML = "";
41056         this.el.remove();
41057         this.el = null;
41058     },
41059     
41060     /**
41061      * form - if the content panel contains a form - this is a reference to it.
41062      * @type {Roo.form.Form}
41063      */
41064     form : false,
41065     /**
41066      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41067      *    This contains a reference to it.
41068      * @type {Roo.View}
41069      */
41070     view : false,
41071     
41072       /**
41073      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41074      * <pre><code>
41075
41076 layout.addxtype({
41077        xtype : 'Form',
41078        items: [ .... ]
41079    }
41080 );
41081
41082 </code></pre>
41083      * @param {Object} cfg Xtype definition of item to add.
41084      */
41085     
41086     
41087     getChildContainer: function () {
41088         return this.getEl();
41089     },
41090     
41091     
41092     onScroll : function(e)
41093     {
41094         this.fireEvent('scroll', this, e);
41095     }
41096     
41097     
41098     /*
41099         var  ret = new Roo.factory(cfg);
41100         return ret;
41101         
41102         
41103         // add form..
41104         if (cfg.xtype.match(/^Form$/)) {
41105             
41106             var el;
41107             //if (this.footer) {
41108             //    el = this.footer.container.insertSibling(false, 'before');
41109             //} else {
41110                 el = this.el.createChild();
41111             //}
41112
41113             this.form = new  Roo.form.Form(cfg);
41114             
41115             
41116             if ( this.form.allItems.length) {
41117                 this.form.render(el.dom);
41118             }
41119             return this.form;
41120         }
41121         // should only have one of theses..
41122         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41123             // views.. should not be just added - used named prop 'view''
41124             
41125             cfg.el = this.el.appendChild(document.createElement("div"));
41126             // factory?
41127             
41128             var ret = new Roo.factory(cfg);
41129              
41130              ret.render && ret.render(false, ''); // render blank..
41131             this.view = ret;
41132             return ret;
41133         }
41134         return false;
41135     }
41136     \*/
41137 });
41138  
41139 /**
41140  * @class Roo.bootstrap.panel.Grid
41141  * @extends Roo.bootstrap.panel.Content
41142  * @constructor
41143  * Create a new GridPanel.
41144  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41145  * @param {Object} config A the config object
41146   
41147  */
41148
41149
41150
41151 Roo.bootstrap.panel.Grid = function(config)
41152 {
41153     
41154       
41155     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41156         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41157
41158     config.el = this.wrapper;
41159     //this.el = this.wrapper;
41160     
41161       if (config.container) {
41162         // ctor'ed from a Border/panel.grid
41163         
41164         
41165         this.wrapper.setStyle("overflow", "hidden");
41166         this.wrapper.addClass('roo-grid-container');
41167
41168     }
41169     
41170     
41171     if(config.toolbar){
41172         var tool_el = this.wrapper.createChild();    
41173         this.toolbar = Roo.factory(config.toolbar);
41174         var ti = [];
41175         if (config.toolbar.items) {
41176             ti = config.toolbar.items ;
41177             delete config.toolbar.items ;
41178         }
41179         
41180         var nitems = [];
41181         this.toolbar.render(tool_el);
41182         for(var i =0;i < ti.length;i++) {
41183           //  Roo.log(['add child', items[i]]);
41184             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41185         }
41186         this.toolbar.items = nitems;
41187         
41188         delete config.toolbar;
41189     }
41190     
41191     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41192     config.grid.scrollBody = true;;
41193     config.grid.monitorWindowResize = false; // turn off autosizing
41194     config.grid.autoHeight = false;
41195     config.grid.autoWidth = false;
41196     
41197     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41198     
41199     if (config.background) {
41200         // render grid on panel activation (if panel background)
41201         this.on('activate', function(gp) {
41202             if (!gp.grid.rendered) {
41203                 gp.grid.render(this.wrapper);
41204                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41205             }
41206         });
41207             
41208     } else {
41209         this.grid.render(this.wrapper);
41210         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41211
41212     }
41213     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41214     // ??? needed ??? config.el = this.wrapper;
41215     
41216     
41217     
41218   
41219     // xtype created footer. - not sure if will work as we normally have to render first..
41220     if (this.footer && !this.footer.el && this.footer.xtype) {
41221         
41222         var ctr = this.grid.getView().getFooterPanel(true);
41223         this.footer.dataSource = this.grid.dataSource;
41224         this.footer = Roo.factory(this.footer, Roo);
41225         this.footer.render(ctr);
41226         
41227     }
41228     
41229     
41230     
41231     
41232      
41233 };
41234
41235 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41236     getId : function(){
41237         return this.grid.id;
41238     },
41239     
41240     /**
41241      * Returns the grid for this panel
41242      * @return {Roo.bootstrap.Table} 
41243      */
41244     getGrid : function(){
41245         return this.grid;    
41246     },
41247     
41248     setSize : function(width, height){
41249         if(!this.ignoreResize(width, height)){
41250             var grid = this.grid;
41251             var size = this.adjustForComponents(width, height);
41252             // tfoot is not a footer?
41253           
41254             
41255             var gridel = grid.getGridEl();
41256             gridel.setSize(size.width, size.height);
41257             
41258             var tbd = grid.getGridEl().select('tbody', true).first();
41259             var thd = grid.getGridEl().select('thead',true).first();
41260             var tbf= grid.getGridEl().select('tfoot', true).first();
41261
41262             if (tbf) {
41263                 size.height -= tbf.getHeight();
41264             }
41265             if (thd) {
41266                 size.height -= thd.getHeight();
41267             }
41268             
41269             tbd.setSize(size.width, size.height );
41270             // this is for the account management tab -seems to work there.
41271             var thd = grid.getGridEl().select('thead',true).first();
41272             //if (tbd) {
41273             //    tbd.setSize(size.width, size.height - thd.getHeight());
41274             //}
41275              
41276             grid.autoSize();
41277         }
41278     },
41279      
41280     
41281     
41282     beforeSlide : function(){
41283         this.grid.getView().scroller.clip();
41284     },
41285     
41286     afterSlide : function(){
41287         this.grid.getView().scroller.unclip();
41288     },
41289     
41290     destroy : function(){
41291         this.grid.destroy();
41292         delete this.grid;
41293         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41294     }
41295 });
41296
41297 /**
41298  * @class Roo.bootstrap.panel.Nest
41299  * @extends Roo.bootstrap.panel.Content
41300  * @constructor
41301  * Create a new Panel, that can contain a layout.Border.
41302  * 
41303  * 
41304  * @param {Roo.BorderLayout} layout The layout for this panel
41305  * @param {String/Object} config A string to set only the title or a config object
41306  */
41307 Roo.bootstrap.panel.Nest = function(config)
41308 {
41309     // construct with only one argument..
41310     /* FIXME - implement nicer consturctors
41311     if (layout.layout) {
41312         config = layout;
41313         layout = config.layout;
41314         delete config.layout;
41315     }
41316     if (layout.xtype && !layout.getEl) {
41317         // then layout needs constructing..
41318         layout = Roo.factory(layout, Roo);
41319     }
41320     */
41321     
41322     config.el =  config.layout.getEl();
41323     
41324     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41325     
41326     config.layout.monitorWindowResize = false; // turn off autosizing
41327     this.layout = config.layout;
41328     this.layout.getEl().addClass("roo-layout-nested-layout");
41329     this.layout.parent = this;
41330     
41331     
41332     
41333     
41334 };
41335
41336 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41337
41338     setSize : function(width, height){
41339         if(!this.ignoreResize(width, height)){
41340             var size = this.adjustForComponents(width, height);
41341             var el = this.layout.getEl();
41342             if (size.height < 1) {
41343                 el.setWidth(size.width);   
41344             } else {
41345                 el.setSize(size.width, size.height);
41346             }
41347             var touch = el.dom.offsetWidth;
41348             this.layout.layout();
41349             // ie requires a double layout on the first pass
41350             if(Roo.isIE && !this.initialized){
41351                 this.initialized = true;
41352                 this.layout.layout();
41353             }
41354         }
41355     },
41356     
41357     // activate all subpanels if not currently active..
41358     
41359     setActiveState : function(active){
41360         this.active = active;
41361         this.setActiveClass(active);
41362         
41363         if(!active){
41364             this.fireEvent("deactivate", this);
41365             return;
41366         }
41367         
41368         this.fireEvent("activate", this);
41369         // not sure if this should happen before or after..
41370         if (!this.layout) {
41371             return; // should not happen..
41372         }
41373         var reg = false;
41374         for (var r in this.layout.regions) {
41375             reg = this.layout.getRegion(r);
41376             if (reg.getActivePanel()) {
41377                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41378                 reg.setActivePanel(reg.getActivePanel());
41379                 continue;
41380             }
41381             if (!reg.panels.length) {
41382                 continue;
41383             }
41384             reg.showPanel(reg.getPanel(0));
41385         }
41386         
41387         
41388         
41389         
41390     },
41391     
41392     /**
41393      * Returns the nested BorderLayout for this panel
41394      * @return {Roo.BorderLayout} 
41395      */
41396     getLayout : function(){
41397         return this.layout;
41398     },
41399     
41400      /**
41401      * Adds a xtype elements to the layout of the nested panel
41402      * <pre><code>
41403
41404 panel.addxtype({
41405        xtype : 'ContentPanel',
41406        region: 'west',
41407        items: [ .... ]
41408    }
41409 );
41410
41411 panel.addxtype({
41412         xtype : 'NestedLayoutPanel',
41413         region: 'west',
41414         layout: {
41415            center: { },
41416            west: { }   
41417         },
41418         items : [ ... list of content panels or nested layout panels.. ]
41419    }
41420 );
41421 </code></pre>
41422      * @param {Object} cfg Xtype definition of item to add.
41423      */
41424     addxtype : function(cfg) {
41425         return this.layout.addxtype(cfg);
41426     
41427     }
41428 });/*
41429  * Based on:
41430  * Ext JS Library 1.1.1
41431  * Copyright(c) 2006-2007, Ext JS, LLC.
41432  *
41433  * Originally Released Under LGPL - original licence link has changed is not relivant.
41434  *
41435  * Fork - LGPL
41436  * <script type="text/javascript">
41437  */
41438 /**
41439  * @class Roo.TabPanel
41440  * @extends Roo.util.Observable
41441  * A lightweight tab container.
41442  * <br><br>
41443  * Usage:
41444  * <pre><code>
41445 // basic tabs 1, built from existing content
41446 var tabs = new Roo.TabPanel("tabs1");
41447 tabs.addTab("script", "View Script");
41448 tabs.addTab("markup", "View Markup");
41449 tabs.activate("script");
41450
41451 // more advanced tabs, built from javascript
41452 var jtabs = new Roo.TabPanel("jtabs");
41453 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41454
41455 // set up the UpdateManager
41456 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41457 var updater = tab2.getUpdateManager();
41458 updater.setDefaultUrl("ajax1.htm");
41459 tab2.on('activate', updater.refresh, updater, true);
41460
41461 // Use setUrl for Ajax loading
41462 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41463 tab3.setUrl("ajax2.htm", null, true);
41464
41465 // Disabled tab
41466 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41467 tab4.disable();
41468
41469 jtabs.activate("jtabs-1");
41470  * </code></pre>
41471  * @constructor
41472  * Create a new TabPanel.
41473  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41474  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41475  */
41476 Roo.bootstrap.panel.Tabs = function(config){
41477     /**
41478     * The container element for this TabPanel.
41479     * @type Roo.Element
41480     */
41481     this.el = Roo.get(config.el);
41482     delete config.el;
41483     if(config){
41484         if(typeof config == "boolean"){
41485             this.tabPosition = config ? "bottom" : "top";
41486         }else{
41487             Roo.apply(this, config);
41488         }
41489     }
41490     
41491     if(this.tabPosition == "bottom"){
41492         // if tabs are at the bottom = create the body first.
41493         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41494         this.el.addClass("roo-tabs-bottom");
41495     }
41496     // next create the tabs holders
41497     
41498     if (this.tabPosition == "west"){
41499         
41500         var reg = this.region; // fake it..
41501         while (reg) {
41502             if (!reg.mgr.parent) {
41503                 break;
41504             }
41505             reg = reg.mgr.parent.region;
41506         }
41507         Roo.log("got nest?");
41508         Roo.log(reg);
41509         if (reg.mgr.getRegion('west')) {
41510             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41511             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41512             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41513             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41514             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41515         
41516             
41517         }
41518         
41519         
41520     } else {
41521      
41522         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41523         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41524         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41525         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41526     }
41527     
41528     
41529     if(Roo.isIE){
41530         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41531     }
41532     
41533     // finally - if tabs are at the top, then create the body last..
41534     if(this.tabPosition != "bottom"){
41535         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41536          * @type Roo.Element
41537          */
41538         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41539         this.el.addClass("roo-tabs-top");
41540     }
41541     this.items = [];
41542
41543     this.bodyEl.setStyle("position", "relative");
41544
41545     this.active = null;
41546     this.activateDelegate = this.activate.createDelegate(this);
41547
41548     this.addEvents({
41549         /**
41550          * @event tabchange
41551          * Fires when the active tab changes
41552          * @param {Roo.TabPanel} this
41553          * @param {Roo.TabPanelItem} activePanel The new active tab
41554          */
41555         "tabchange": true,
41556         /**
41557          * @event beforetabchange
41558          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41559          * @param {Roo.TabPanel} this
41560          * @param {Object} e Set cancel to true on this object to cancel the tab change
41561          * @param {Roo.TabPanelItem} tab The tab being changed to
41562          */
41563         "beforetabchange" : true
41564     });
41565
41566     Roo.EventManager.onWindowResize(this.onResize, this);
41567     this.cpad = this.el.getPadding("lr");
41568     this.hiddenCount = 0;
41569
41570
41571     // toolbar on the tabbar support...
41572     if (this.toolbar) {
41573         alert("no toolbar support yet");
41574         this.toolbar  = false;
41575         /*
41576         var tcfg = this.toolbar;
41577         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41578         this.toolbar = new Roo.Toolbar(tcfg);
41579         if (Roo.isSafari) {
41580             var tbl = tcfg.container.child('table', true);
41581             tbl.setAttribute('width', '100%');
41582         }
41583         */
41584         
41585     }
41586    
41587
41588
41589     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41590 };
41591
41592 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41593     /*
41594      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41595      */
41596     tabPosition : "top",
41597     /*
41598      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41599      */
41600     currentTabWidth : 0,
41601     /*
41602      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41603      */
41604     minTabWidth : 40,
41605     /*
41606      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41607      */
41608     maxTabWidth : 250,
41609     /*
41610      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41611      */
41612     preferredTabWidth : 175,
41613     /*
41614      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41615      */
41616     resizeTabs : false,
41617     /*
41618      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41619      */
41620     monitorResize : true,
41621     /*
41622      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41623      */
41624     toolbar : false,  // set by caller..
41625     
41626     region : false, /// set by caller
41627     
41628     disableTooltips : true, // not used yet...
41629
41630     /**
41631      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41632      * @param {String} id The id of the div to use <b>or create</b>
41633      * @param {String} text The text for the tab
41634      * @param {String} content (optional) Content to put in the TabPanelItem body
41635      * @param {Boolean} closable (optional) True to create a close icon on the tab
41636      * @return {Roo.TabPanelItem} The created TabPanelItem
41637      */
41638     addTab : function(id, text, content, closable, tpl)
41639     {
41640         var item = new Roo.bootstrap.panel.TabItem({
41641             panel: this,
41642             id : id,
41643             text : text,
41644             closable : closable,
41645             tpl : tpl
41646         });
41647         this.addTabItem(item);
41648         if(content){
41649             item.setContent(content);
41650         }
41651         return item;
41652     },
41653
41654     /**
41655      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41656      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41657      * @return {Roo.TabPanelItem}
41658      */
41659     getTab : function(id){
41660         return this.items[id];
41661     },
41662
41663     /**
41664      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41665      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41666      */
41667     hideTab : function(id){
41668         var t = this.items[id];
41669         if(!t.isHidden()){
41670            t.setHidden(true);
41671            this.hiddenCount++;
41672            this.autoSizeTabs();
41673         }
41674     },
41675
41676     /**
41677      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41678      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41679      */
41680     unhideTab : function(id){
41681         var t = this.items[id];
41682         if(t.isHidden()){
41683            t.setHidden(false);
41684            this.hiddenCount--;
41685            this.autoSizeTabs();
41686         }
41687     },
41688
41689     /**
41690      * Adds an existing {@link Roo.TabPanelItem}.
41691      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41692      */
41693     addTabItem : function(item)
41694     {
41695         this.items[item.id] = item;
41696         this.items.push(item);
41697         this.autoSizeTabs();
41698       //  if(this.resizeTabs){
41699     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41700   //         this.autoSizeTabs();
41701 //        }else{
41702 //            item.autoSize();
41703        // }
41704     },
41705
41706     /**
41707      * Removes a {@link Roo.TabPanelItem}.
41708      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41709      */
41710     removeTab : function(id){
41711         var items = this.items;
41712         var tab = items[id];
41713         if(!tab) { return; }
41714         var index = items.indexOf(tab);
41715         if(this.active == tab && items.length > 1){
41716             var newTab = this.getNextAvailable(index);
41717             if(newTab) {
41718                 newTab.activate();
41719             }
41720         }
41721         this.stripEl.dom.removeChild(tab.pnode.dom);
41722         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41723             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41724         }
41725         items.splice(index, 1);
41726         delete this.items[tab.id];
41727         tab.fireEvent("close", tab);
41728         tab.purgeListeners();
41729         this.autoSizeTabs();
41730     },
41731
41732     getNextAvailable : function(start){
41733         var items = this.items;
41734         var index = start;
41735         // look for a next tab that will slide over to
41736         // replace the one being removed
41737         while(index < items.length){
41738             var item = items[++index];
41739             if(item && !item.isHidden()){
41740                 return item;
41741             }
41742         }
41743         // if one isn't found select the previous tab (on the left)
41744         index = start;
41745         while(index >= 0){
41746             var item = items[--index];
41747             if(item && !item.isHidden()){
41748                 return item;
41749             }
41750         }
41751         return null;
41752     },
41753
41754     /**
41755      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41756      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41757      */
41758     disableTab : function(id){
41759         var tab = this.items[id];
41760         if(tab && this.active != tab){
41761             tab.disable();
41762         }
41763     },
41764
41765     /**
41766      * Enables a {@link Roo.TabPanelItem} that is disabled.
41767      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41768      */
41769     enableTab : function(id){
41770         var tab = this.items[id];
41771         tab.enable();
41772     },
41773
41774     /**
41775      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41776      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41777      * @return {Roo.TabPanelItem} The TabPanelItem.
41778      */
41779     activate : function(id)
41780     {
41781         //Roo.log('activite:'  + id);
41782         
41783         var tab = this.items[id];
41784         if(!tab){
41785             return null;
41786         }
41787         if(tab == this.active || tab.disabled){
41788             return tab;
41789         }
41790         var e = {};
41791         this.fireEvent("beforetabchange", this, e, tab);
41792         if(e.cancel !== true && !tab.disabled){
41793             if(this.active){
41794                 this.active.hide();
41795             }
41796             this.active = this.items[id];
41797             this.active.show();
41798             this.fireEvent("tabchange", this, this.active);
41799         }
41800         return tab;
41801     },
41802
41803     /**
41804      * Gets the active {@link Roo.TabPanelItem}.
41805      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41806      */
41807     getActiveTab : function(){
41808         return this.active;
41809     },
41810
41811     /**
41812      * Updates the tab body element to fit the height of the container element
41813      * for overflow scrolling
41814      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41815      */
41816     syncHeight : function(targetHeight){
41817         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41818         var bm = this.bodyEl.getMargins();
41819         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41820         this.bodyEl.setHeight(newHeight);
41821         return newHeight;
41822     },
41823
41824     onResize : function(){
41825         if(this.monitorResize){
41826             this.autoSizeTabs();
41827         }
41828     },
41829
41830     /**
41831      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41832      */
41833     beginUpdate : function(){
41834         this.updating = true;
41835     },
41836
41837     /**
41838      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41839      */
41840     endUpdate : function(){
41841         this.updating = false;
41842         this.autoSizeTabs();
41843     },
41844
41845     /**
41846      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41847      */
41848     autoSizeTabs : function()
41849     {
41850         var count = this.items.length;
41851         var vcount = count - this.hiddenCount;
41852         
41853         if (vcount < 2) {
41854             this.stripEl.hide();
41855         } else {
41856             this.stripEl.show();
41857         }
41858         
41859         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41860             return;
41861         }
41862         
41863         
41864         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41865         var availWidth = Math.floor(w / vcount);
41866         var b = this.stripBody;
41867         if(b.getWidth() > w){
41868             var tabs = this.items;
41869             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41870             if(availWidth < this.minTabWidth){
41871                 /*if(!this.sleft){    // incomplete scrolling code
41872                     this.createScrollButtons();
41873                 }
41874                 this.showScroll();
41875                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41876             }
41877         }else{
41878             if(this.currentTabWidth < this.preferredTabWidth){
41879                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41880             }
41881         }
41882     },
41883
41884     /**
41885      * Returns the number of tabs in this TabPanel.
41886      * @return {Number}
41887      */
41888      getCount : function(){
41889          return this.items.length;
41890      },
41891
41892     /**
41893      * Resizes all the tabs to the passed width
41894      * @param {Number} The new width
41895      */
41896     setTabWidth : function(width){
41897         this.currentTabWidth = width;
41898         for(var i = 0, len = this.items.length; i < len; i++) {
41899                 if(!this.items[i].isHidden()) {
41900                 this.items[i].setWidth(width);
41901             }
41902         }
41903     },
41904
41905     /**
41906      * Destroys this TabPanel
41907      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41908      */
41909     destroy : function(removeEl){
41910         Roo.EventManager.removeResizeListener(this.onResize, this);
41911         for(var i = 0, len = this.items.length; i < len; i++){
41912             this.items[i].purgeListeners();
41913         }
41914         if(removeEl === true){
41915             this.el.update("");
41916             this.el.remove();
41917         }
41918     },
41919     
41920     createStrip : function(container)
41921     {
41922         var strip = document.createElement("nav");
41923         strip.className = Roo.bootstrap.version == 4 ?
41924             "navbar-light bg-light" : 
41925             "navbar navbar-default"; //"x-tabs-wrap";
41926         container.appendChild(strip);
41927         return strip;
41928     },
41929     
41930     createStripList : function(strip)
41931     {
41932         // div wrapper for retard IE
41933         // returns the "tr" element.
41934         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41935         //'<div class="x-tabs-strip-wrap">'+
41936           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41937           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41938         return strip.firstChild; //.firstChild.firstChild.firstChild;
41939     },
41940     createBody : function(container)
41941     {
41942         var body = document.createElement("div");
41943         Roo.id(body, "tab-body");
41944         //Roo.fly(body).addClass("x-tabs-body");
41945         Roo.fly(body).addClass("tab-content");
41946         container.appendChild(body);
41947         return body;
41948     },
41949     createItemBody :function(bodyEl, id){
41950         var body = Roo.getDom(id);
41951         if(!body){
41952             body = document.createElement("div");
41953             body.id = id;
41954         }
41955         //Roo.fly(body).addClass("x-tabs-item-body");
41956         Roo.fly(body).addClass("tab-pane");
41957          bodyEl.insertBefore(body, bodyEl.firstChild);
41958         return body;
41959     },
41960     /** @private */
41961     createStripElements :  function(stripEl, text, closable, tpl)
41962     {
41963         var td = document.createElement("li"); // was td..
41964         td.className = 'nav-item';
41965         
41966         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41967         
41968         
41969         stripEl.appendChild(td);
41970         /*if(closable){
41971             td.className = "x-tabs-closable";
41972             if(!this.closeTpl){
41973                 this.closeTpl = new Roo.Template(
41974                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41975                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41976                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41977                 );
41978             }
41979             var el = this.closeTpl.overwrite(td, {"text": text});
41980             var close = el.getElementsByTagName("div")[0];
41981             var inner = el.getElementsByTagName("em")[0];
41982             return {"el": el, "close": close, "inner": inner};
41983         } else {
41984         */
41985         // not sure what this is..
41986 //            if(!this.tabTpl){
41987                 //this.tabTpl = new Roo.Template(
41988                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41989                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41990                 //);
41991 //                this.tabTpl = new Roo.Template(
41992 //                   '<a href="#">' +
41993 //                   '<span unselectable="on"' +
41994 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41995 //                            ' >{text}</span></a>'
41996 //                );
41997 //                
41998 //            }
41999
42000
42001             var template = tpl || this.tabTpl || false;
42002             
42003             if(!template){
42004                 template =  new Roo.Template(
42005                         Roo.bootstrap.version == 4 ? 
42006                             (
42007                                 '<a class="nav-link" href="#" unselectable="on"' +
42008                                      (this.disableTooltips ? '' : ' title="{text}"') +
42009                                      ' >{text}</a>'
42010                             ) : (
42011                                 '<a class="nav-link" href="#">' +
42012                                 '<span unselectable="on"' +
42013                                          (this.disableTooltips ? '' : ' title="{text}"') +
42014                                     ' >{text}</span></a>'
42015                             )
42016                 );
42017             }
42018             
42019             switch (typeof(template)) {
42020                 case 'object' :
42021                     break;
42022                 case 'string' :
42023                     template = new Roo.Template(template);
42024                     break;
42025                 default :
42026                     break;
42027             }
42028             
42029             var el = template.overwrite(td, {"text": text});
42030             
42031             var inner = el.getElementsByTagName("span")[0];
42032             
42033             return {"el": el, "inner": inner};
42034             
42035     }
42036         
42037     
42038 });
42039
42040 /**
42041  * @class Roo.TabPanelItem
42042  * @extends Roo.util.Observable
42043  * Represents an individual item (tab plus body) in a TabPanel.
42044  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42045  * @param {String} id The id of this TabPanelItem
42046  * @param {String} text The text for the tab of this TabPanelItem
42047  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42048  */
42049 Roo.bootstrap.panel.TabItem = function(config){
42050     /**
42051      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42052      * @type Roo.TabPanel
42053      */
42054     this.tabPanel = config.panel;
42055     /**
42056      * The id for this TabPanelItem
42057      * @type String
42058      */
42059     this.id = config.id;
42060     /** @private */
42061     this.disabled = false;
42062     /** @private */
42063     this.text = config.text;
42064     /** @private */
42065     this.loaded = false;
42066     this.closable = config.closable;
42067
42068     /**
42069      * The body element for this TabPanelItem.
42070      * @type Roo.Element
42071      */
42072     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42073     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42074     this.bodyEl.setStyle("display", "block");
42075     this.bodyEl.setStyle("zoom", "1");
42076     //this.hideAction();
42077
42078     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42079     /** @private */
42080     this.el = Roo.get(els.el);
42081     this.inner = Roo.get(els.inner, true);
42082      this.textEl = Roo.bootstrap.version == 4 ?
42083         this.el : Roo.get(this.el.dom.firstChild, true);
42084
42085     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42086     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42087
42088     
42089 //    this.el.on("mousedown", this.onTabMouseDown, this);
42090     this.el.on("click", this.onTabClick, this);
42091     /** @private */
42092     if(config.closable){
42093         var c = Roo.get(els.close, true);
42094         c.dom.title = this.closeText;
42095         c.addClassOnOver("close-over");
42096         c.on("click", this.closeClick, this);
42097      }
42098
42099     this.addEvents({
42100          /**
42101          * @event activate
42102          * Fires when this tab becomes the active tab.
42103          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42104          * @param {Roo.TabPanelItem} this
42105          */
42106         "activate": true,
42107         /**
42108          * @event beforeclose
42109          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42110          * @param {Roo.TabPanelItem} this
42111          * @param {Object} e Set cancel to true on this object to cancel the close.
42112          */
42113         "beforeclose": true,
42114         /**
42115          * @event close
42116          * Fires when this tab is closed.
42117          * @param {Roo.TabPanelItem} this
42118          */
42119          "close": true,
42120         /**
42121          * @event deactivate
42122          * Fires when this tab is no longer the active tab.
42123          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42124          * @param {Roo.TabPanelItem} this
42125          */
42126          "deactivate" : true
42127     });
42128     this.hidden = false;
42129
42130     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42131 };
42132
42133 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42134            {
42135     purgeListeners : function(){
42136        Roo.util.Observable.prototype.purgeListeners.call(this);
42137        this.el.removeAllListeners();
42138     },
42139     /**
42140      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42141      */
42142     show : function(){
42143         this.status_node.addClass("active");
42144         this.showAction();
42145         if(Roo.isOpera){
42146             this.tabPanel.stripWrap.repaint();
42147         }
42148         this.fireEvent("activate", this.tabPanel, this);
42149     },
42150
42151     /**
42152      * Returns true if this tab is the active tab.
42153      * @return {Boolean}
42154      */
42155     isActive : function(){
42156         return this.tabPanel.getActiveTab() == this;
42157     },
42158
42159     /**
42160      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42161      */
42162     hide : function(){
42163         this.status_node.removeClass("active");
42164         this.hideAction();
42165         this.fireEvent("deactivate", this.tabPanel, this);
42166     },
42167
42168     hideAction : function(){
42169         this.bodyEl.hide();
42170         this.bodyEl.setStyle("position", "absolute");
42171         this.bodyEl.setLeft("-20000px");
42172         this.bodyEl.setTop("-20000px");
42173     },
42174
42175     showAction : function(){
42176         this.bodyEl.setStyle("position", "relative");
42177         this.bodyEl.setTop("");
42178         this.bodyEl.setLeft("");
42179         this.bodyEl.show();
42180     },
42181
42182     /**
42183      * Set the tooltip for the tab.
42184      * @param {String} tooltip The tab's tooltip
42185      */
42186     setTooltip : function(text){
42187         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42188             this.textEl.dom.qtip = text;
42189             this.textEl.dom.removeAttribute('title');
42190         }else{
42191             this.textEl.dom.title = text;
42192         }
42193     },
42194
42195     onTabClick : function(e){
42196         e.preventDefault();
42197         this.tabPanel.activate(this.id);
42198     },
42199
42200     onTabMouseDown : function(e){
42201         e.preventDefault();
42202         this.tabPanel.activate(this.id);
42203     },
42204 /*
42205     getWidth : function(){
42206         return this.inner.getWidth();
42207     },
42208
42209     setWidth : function(width){
42210         var iwidth = width - this.linode.getPadding("lr");
42211         this.inner.setWidth(iwidth);
42212         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42213         this.linode.setWidth(width);
42214     },
42215 */
42216     /**
42217      * Show or hide the tab
42218      * @param {Boolean} hidden True to hide or false to show.
42219      */
42220     setHidden : function(hidden){
42221         this.hidden = hidden;
42222         this.linode.setStyle("display", hidden ? "none" : "");
42223     },
42224
42225     /**
42226      * Returns true if this tab is "hidden"
42227      * @return {Boolean}
42228      */
42229     isHidden : function(){
42230         return this.hidden;
42231     },
42232
42233     /**
42234      * Returns the text for this tab
42235      * @return {String}
42236      */
42237     getText : function(){
42238         return this.text;
42239     },
42240     /*
42241     autoSize : function(){
42242         //this.el.beginMeasure();
42243         this.textEl.setWidth(1);
42244         /*
42245          *  #2804 [new] Tabs in Roojs
42246          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42247          */
42248         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42249         //this.el.endMeasure();
42250     //},
42251
42252     /**
42253      * Sets the text for the tab (Note: this also sets the tooltip text)
42254      * @param {String} text The tab's text and tooltip
42255      */
42256     setText : function(text){
42257         this.text = text;
42258         this.textEl.update(text);
42259         this.setTooltip(text);
42260         //if(!this.tabPanel.resizeTabs){
42261         //    this.autoSize();
42262         //}
42263     },
42264     /**
42265      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42266      */
42267     activate : function(){
42268         this.tabPanel.activate(this.id);
42269     },
42270
42271     /**
42272      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42273      */
42274     disable : function(){
42275         if(this.tabPanel.active != this){
42276             this.disabled = true;
42277             this.status_node.addClass("disabled");
42278         }
42279     },
42280
42281     /**
42282      * Enables this TabPanelItem if it was previously disabled.
42283      */
42284     enable : function(){
42285         this.disabled = false;
42286         this.status_node.removeClass("disabled");
42287     },
42288
42289     /**
42290      * Sets the content for this TabPanelItem.
42291      * @param {String} content The content
42292      * @param {Boolean} loadScripts true to look for and load scripts
42293      */
42294     setContent : function(content, loadScripts){
42295         this.bodyEl.update(content, loadScripts);
42296     },
42297
42298     /**
42299      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42300      * @return {Roo.UpdateManager} The UpdateManager
42301      */
42302     getUpdateManager : function(){
42303         return this.bodyEl.getUpdateManager();
42304     },
42305
42306     /**
42307      * Set a URL to be used to load the content for this TabPanelItem.
42308      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42309      * @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)
42310      * @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)
42311      * @return {Roo.UpdateManager} The UpdateManager
42312      */
42313     setUrl : function(url, params, loadOnce){
42314         if(this.refreshDelegate){
42315             this.un('activate', this.refreshDelegate);
42316         }
42317         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42318         this.on("activate", this.refreshDelegate);
42319         return this.bodyEl.getUpdateManager();
42320     },
42321
42322     /** @private */
42323     _handleRefresh : function(url, params, loadOnce){
42324         if(!loadOnce || !this.loaded){
42325             var updater = this.bodyEl.getUpdateManager();
42326             updater.update(url, params, this._setLoaded.createDelegate(this));
42327         }
42328     },
42329
42330     /**
42331      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42332      *   Will fail silently if the setUrl method has not been called.
42333      *   This does not activate the panel, just updates its content.
42334      */
42335     refresh : function(){
42336         if(this.refreshDelegate){
42337            this.loaded = false;
42338            this.refreshDelegate();
42339         }
42340     },
42341
42342     /** @private */
42343     _setLoaded : function(){
42344         this.loaded = true;
42345     },
42346
42347     /** @private */
42348     closeClick : function(e){
42349         var o = {};
42350         e.stopEvent();
42351         this.fireEvent("beforeclose", this, o);
42352         if(o.cancel !== true){
42353             this.tabPanel.removeTab(this.id);
42354         }
42355     },
42356     /**
42357      * The text displayed in the tooltip for the close icon.
42358      * @type String
42359      */
42360     closeText : "Close this tab"
42361 });
42362 /**
42363 *    This script refer to:
42364 *    Title: International Telephone Input
42365 *    Author: Jack O'Connor
42366 *    Code version:  v12.1.12
42367 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42368 **/
42369
42370 Roo.bootstrap.PhoneInputData = function() {
42371     var d = [
42372       [
42373         "Afghanistan (‫افغانستان‬‎)",
42374         "af",
42375         "93"
42376       ],
42377       [
42378         "Albania (Shqipëri)",
42379         "al",
42380         "355"
42381       ],
42382       [
42383         "Algeria (‫الجزائر‬‎)",
42384         "dz",
42385         "213"
42386       ],
42387       [
42388         "American Samoa",
42389         "as",
42390         "1684"
42391       ],
42392       [
42393         "Andorra",
42394         "ad",
42395         "376"
42396       ],
42397       [
42398         "Angola",
42399         "ao",
42400         "244"
42401       ],
42402       [
42403         "Anguilla",
42404         "ai",
42405         "1264"
42406       ],
42407       [
42408         "Antigua and Barbuda",
42409         "ag",
42410         "1268"
42411       ],
42412       [
42413         "Argentina",
42414         "ar",
42415         "54"
42416       ],
42417       [
42418         "Armenia (Հայաստան)",
42419         "am",
42420         "374"
42421       ],
42422       [
42423         "Aruba",
42424         "aw",
42425         "297"
42426       ],
42427       [
42428         "Australia",
42429         "au",
42430         "61",
42431         0
42432       ],
42433       [
42434         "Austria (Österreich)",
42435         "at",
42436         "43"
42437       ],
42438       [
42439         "Azerbaijan (Azərbaycan)",
42440         "az",
42441         "994"
42442       ],
42443       [
42444         "Bahamas",
42445         "bs",
42446         "1242"
42447       ],
42448       [
42449         "Bahrain (‫البحرين‬‎)",
42450         "bh",
42451         "973"
42452       ],
42453       [
42454         "Bangladesh (বাংলাদেশ)",
42455         "bd",
42456         "880"
42457       ],
42458       [
42459         "Barbados",
42460         "bb",
42461         "1246"
42462       ],
42463       [
42464         "Belarus (Беларусь)",
42465         "by",
42466         "375"
42467       ],
42468       [
42469         "Belgium (België)",
42470         "be",
42471         "32"
42472       ],
42473       [
42474         "Belize",
42475         "bz",
42476         "501"
42477       ],
42478       [
42479         "Benin (Bénin)",
42480         "bj",
42481         "229"
42482       ],
42483       [
42484         "Bermuda",
42485         "bm",
42486         "1441"
42487       ],
42488       [
42489         "Bhutan (འབྲུག)",
42490         "bt",
42491         "975"
42492       ],
42493       [
42494         "Bolivia",
42495         "bo",
42496         "591"
42497       ],
42498       [
42499         "Bosnia and Herzegovina (Босна и Херцеговина)",
42500         "ba",
42501         "387"
42502       ],
42503       [
42504         "Botswana",
42505         "bw",
42506         "267"
42507       ],
42508       [
42509         "Brazil (Brasil)",
42510         "br",
42511         "55"
42512       ],
42513       [
42514         "British Indian Ocean Territory",
42515         "io",
42516         "246"
42517       ],
42518       [
42519         "British Virgin Islands",
42520         "vg",
42521         "1284"
42522       ],
42523       [
42524         "Brunei",
42525         "bn",
42526         "673"
42527       ],
42528       [
42529         "Bulgaria (България)",
42530         "bg",
42531         "359"
42532       ],
42533       [
42534         "Burkina Faso",
42535         "bf",
42536         "226"
42537       ],
42538       [
42539         "Burundi (Uburundi)",
42540         "bi",
42541         "257"
42542       ],
42543       [
42544         "Cambodia (កម្ពុជា)",
42545         "kh",
42546         "855"
42547       ],
42548       [
42549         "Cameroon (Cameroun)",
42550         "cm",
42551         "237"
42552       ],
42553       [
42554         "Canada",
42555         "ca",
42556         "1",
42557         1,
42558         ["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"]
42559       ],
42560       [
42561         "Cape Verde (Kabu Verdi)",
42562         "cv",
42563         "238"
42564       ],
42565       [
42566         "Caribbean Netherlands",
42567         "bq",
42568         "599",
42569         1
42570       ],
42571       [
42572         "Cayman Islands",
42573         "ky",
42574         "1345"
42575       ],
42576       [
42577         "Central African Republic (République centrafricaine)",
42578         "cf",
42579         "236"
42580       ],
42581       [
42582         "Chad (Tchad)",
42583         "td",
42584         "235"
42585       ],
42586       [
42587         "Chile",
42588         "cl",
42589         "56"
42590       ],
42591       [
42592         "China (中国)",
42593         "cn",
42594         "86"
42595       ],
42596       [
42597         "Christmas Island",
42598         "cx",
42599         "61",
42600         2
42601       ],
42602       [
42603         "Cocos (Keeling) Islands",
42604         "cc",
42605         "61",
42606         1
42607       ],
42608       [
42609         "Colombia",
42610         "co",
42611         "57"
42612       ],
42613       [
42614         "Comoros (‫جزر القمر‬‎)",
42615         "km",
42616         "269"
42617       ],
42618       [
42619         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42620         "cd",
42621         "243"
42622       ],
42623       [
42624         "Congo (Republic) (Congo-Brazzaville)",
42625         "cg",
42626         "242"
42627       ],
42628       [
42629         "Cook Islands",
42630         "ck",
42631         "682"
42632       ],
42633       [
42634         "Costa Rica",
42635         "cr",
42636         "506"
42637       ],
42638       [
42639         "Côte d’Ivoire",
42640         "ci",
42641         "225"
42642       ],
42643       [
42644         "Croatia (Hrvatska)",
42645         "hr",
42646         "385"
42647       ],
42648       [
42649         "Cuba",
42650         "cu",
42651         "53"
42652       ],
42653       [
42654         "Curaçao",
42655         "cw",
42656         "599",
42657         0
42658       ],
42659       [
42660         "Cyprus (Κύπρος)",
42661         "cy",
42662         "357"
42663       ],
42664       [
42665         "Czech Republic (Česká republika)",
42666         "cz",
42667         "420"
42668       ],
42669       [
42670         "Denmark (Danmark)",
42671         "dk",
42672         "45"
42673       ],
42674       [
42675         "Djibouti",
42676         "dj",
42677         "253"
42678       ],
42679       [
42680         "Dominica",
42681         "dm",
42682         "1767"
42683       ],
42684       [
42685         "Dominican Republic (República Dominicana)",
42686         "do",
42687         "1",
42688         2,
42689         ["809", "829", "849"]
42690       ],
42691       [
42692         "Ecuador",
42693         "ec",
42694         "593"
42695       ],
42696       [
42697         "Egypt (‫مصر‬‎)",
42698         "eg",
42699         "20"
42700       ],
42701       [
42702         "El Salvador",
42703         "sv",
42704         "503"
42705       ],
42706       [
42707         "Equatorial Guinea (Guinea Ecuatorial)",
42708         "gq",
42709         "240"
42710       ],
42711       [
42712         "Eritrea",
42713         "er",
42714         "291"
42715       ],
42716       [
42717         "Estonia (Eesti)",
42718         "ee",
42719         "372"
42720       ],
42721       [
42722         "Ethiopia",
42723         "et",
42724         "251"
42725       ],
42726       [
42727         "Falkland Islands (Islas Malvinas)",
42728         "fk",
42729         "500"
42730       ],
42731       [
42732         "Faroe Islands (Føroyar)",
42733         "fo",
42734         "298"
42735       ],
42736       [
42737         "Fiji",
42738         "fj",
42739         "679"
42740       ],
42741       [
42742         "Finland (Suomi)",
42743         "fi",
42744         "358",
42745         0
42746       ],
42747       [
42748         "France",
42749         "fr",
42750         "33"
42751       ],
42752       [
42753         "French Guiana (Guyane française)",
42754         "gf",
42755         "594"
42756       ],
42757       [
42758         "French Polynesia (Polynésie française)",
42759         "pf",
42760         "689"
42761       ],
42762       [
42763         "Gabon",
42764         "ga",
42765         "241"
42766       ],
42767       [
42768         "Gambia",
42769         "gm",
42770         "220"
42771       ],
42772       [
42773         "Georgia (საქართველო)",
42774         "ge",
42775         "995"
42776       ],
42777       [
42778         "Germany (Deutschland)",
42779         "de",
42780         "49"
42781       ],
42782       [
42783         "Ghana (Gaana)",
42784         "gh",
42785         "233"
42786       ],
42787       [
42788         "Gibraltar",
42789         "gi",
42790         "350"
42791       ],
42792       [
42793         "Greece (Ελλάδα)",
42794         "gr",
42795         "30"
42796       ],
42797       [
42798         "Greenland (Kalaallit Nunaat)",
42799         "gl",
42800         "299"
42801       ],
42802       [
42803         "Grenada",
42804         "gd",
42805         "1473"
42806       ],
42807       [
42808         "Guadeloupe",
42809         "gp",
42810         "590",
42811         0
42812       ],
42813       [
42814         "Guam",
42815         "gu",
42816         "1671"
42817       ],
42818       [
42819         "Guatemala",
42820         "gt",
42821         "502"
42822       ],
42823       [
42824         "Guernsey",
42825         "gg",
42826         "44",
42827         1
42828       ],
42829       [
42830         "Guinea (Guinée)",
42831         "gn",
42832         "224"
42833       ],
42834       [
42835         "Guinea-Bissau (Guiné Bissau)",
42836         "gw",
42837         "245"
42838       ],
42839       [
42840         "Guyana",
42841         "gy",
42842         "592"
42843       ],
42844       [
42845         "Haiti",
42846         "ht",
42847         "509"
42848       ],
42849       [
42850         "Honduras",
42851         "hn",
42852         "504"
42853       ],
42854       [
42855         "Hong Kong (香港)",
42856         "hk",
42857         "852"
42858       ],
42859       [
42860         "Hungary (Magyarország)",
42861         "hu",
42862         "36"
42863       ],
42864       [
42865         "Iceland (Ísland)",
42866         "is",
42867         "354"
42868       ],
42869       [
42870         "India (भारत)",
42871         "in",
42872         "91"
42873       ],
42874       [
42875         "Indonesia",
42876         "id",
42877         "62"
42878       ],
42879       [
42880         "Iran (‫ایران‬‎)",
42881         "ir",
42882         "98"
42883       ],
42884       [
42885         "Iraq (‫العراق‬‎)",
42886         "iq",
42887         "964"
42888       ],
42889       [
42890         "Ireland",
42891         "ie",
42892         "353"
42893       ],
42894       [
42895         "Isle of Man",
42896         "im",
42897         "44",
42898         2
42899       ],
42900       [
42901         "Israel (‫ישראל‬‎)",
42902         "il",
42903         "972"
42904       ],
42905       [
42906         "Italy (Italia)",
42907         "it",
42908         "39",
42909         0
42910       ],
42911       [
42912         "Jamaica",
42913         "jm",
42914         "1876"
42915       ],
42916       [
42917         "Japan (日本)",
42918         "jp",
42919         "81"
42920       ],
42921       [
42922         "Jersey",
42923         "je",
42924         "44",
42925         3
42926       ],
42927       [
42928         "Jordan (‫الأردن‬‎)",
42929         "jo",
42930         "962"
42931       ],
42932       [
42933         "Kazakhstan (Казахстан)",
42934         "kz",
42935         "7",
42936         1
42937       ],
42938       [
42939         "Kenya",
42940         "ke",
42941         "254"
42942       ],
42943       [
42944         "Kiribati",
42945         "ki",
42946         "686"
42947       ],
42948       [
42949         "Kosovo",
42950         "xk",
42951         "383"
42952       ],
42953       [
42954         "Kuwait (‫الكويت‬‎)",
42955         "kw",
42956         "965"
42957       ],
42958       [
42959         "Kyrgyzstan (Кыргызстан)",
42960         "kg",
42961         "996"
42962       ],
42963       [
42964         "Laos (ລາວ)",
42965         "la",
42966         "856"
42967       ],
42968       [
42969         "Latvia (Latvija)",
42970         "lv",
42971         "371"
42972       ],
42973       [
42974         "Lebanon (‫لبنان‬‎)",
42975         "lb",
42976         "961"
42977       ],
42978       [
42979         "Lesotho",
42980         "ls",
42981         "266"
42982       ],
42983       [
42984         "Liberia",
42985         "lr",
42986         "231"
42987       ],
42988       [
42989         "Libya (‫ليبيا‬‎)",
42990         "ly",
42991         "218"
42992       ],
42993       [
42994         "Liechtenstein",
42995         "li",
42996         "423"
42997       ],
42998       [
42999         "Lithuania (Lietuva)",
43000         "lt",
43001         "370"
43002       ],
43003       [
43004         "Luxembourg",
43005         "lu",
43006         "352"
43007       ],
43008       [
43009         "Macau (澳門)",
43010         "mo",
43011         "853"
43012       ],
43013       [
43014         "Macedonia (FYROM) (Македонија)",
43015         "mk",
43016         "389"
43017       ],
43018       [
43019         "Madagascar (Madagasikara)",
43020         "mg",
43021         "261"
43022       ],
43023       [
43024         "Malawi",
43025         "mw",
43026         "265"
43027       ],
43028       [
43029         "Malaysia",
43030         "my",
43031         "60"
43032       ],
43033       [
43034         "Maldives",
43035         "mv",
43036         "960"
43037       ],
43038       [
43039         "Mali",
43040         "ml",
43041         "223"
43042       ],
43043       [
43044         "Malta",
43045         "mt",
43046         "356"
43047       ],
43048       [
43049         "Marshall Islands",
43050         "mh",
43051         "692"
43052       ],
43053       [
43054         "Martinique",
43055         "mq",
43056         "596"
43057       ],
43058       [
43059         "Mauritania (‫موريتانيا‬‎)",
43060         "mr",
43061         "222"
43062       ],
43063       [
43064         "Mauritius (Moris)",
43065         "mu",
43066         "230"
43067       ],
43068       [
43069         "Mayotte",
43070         "yt",
43071         "262",
43072         1
43073       ],
43074       [
43075         "Mexico (México)",
43076         "mx",
43077         "52"
43078       ],
43079       [
43080         "Micronesia",
43081         "fm",
43082         "691"
43083       ],
43084       [
43085         "Moldova (Republica Moldova)",
43086         "md",
43087         "373"
43088       ],
43089       [
43090         "Monaco",
43091         "mc",
43092         "377"
43093       ],
43094       [
43095         "Mongolia (Монгол)",
43096         "mn",
43097         "976"
43098       ],
43099       [
43100         "Montenegro (Crna Gora)",
43101         "me",
43102         "382"
43103       ],
43104       [
43105         "Montserrat",
43106         "ms",
43107         "1664"
43108       ],
43109       [
43110         "Morocco (‫المغرب‬‎)",
43111         "ma",
43112         "212",
43113         0
43114       ],
43115       [
43116         "Mozambique (Moçambique)",
43117         "mz",
43118         "258"
43119       ],
43120       [
43121         "Myanmar (Burma) (မြန်မာ)",
43122         "mm",
43123         "95"
43124       ],
43125       [
43126         "Namibia (Namibië)",
43127         "na",
43128         "264"
43129       ],
43130       [
43131         "Nauru",
43132         "nr",
43133         "674"
43134       ],
43135       [
43136         "Nepal (नेपाल)",
43137         "np",
43138         "977"
43139       ],
43140       [
43141         "Netherlands (Nederland)",
43142         "nl",
43143         "31"
43144       ],
43145       [
43146         "New Caledonia (Nouvelle-Calédonie)",
43147         "nc",
43148         "687"
43149       ],
43150       [
43151         "New Zealand",
43152         "nz",
43153         "64"
43154       ],
43155       [
43156         "Nicaragua",
43157         "ni",
43158         "505"
43159       ],
43160       [
43161         "Niger (Nijar)",
43162         "ne",
43163         "227"
43164       ],
43165       [
43166         "Nigeria",
43167         "ng",
43168         "234"
43169       ],
43170       [
43171         "Niue",
43172         "nu",
43173         "683"
43174       ],
43175       [
43176         "Norfolk Island",
43177         "nf",
43178         "672"
43179       ],
43180       [
43181         "North Korea (조선 민주주의 인민 공화국)",
43182         "kp",
43183         "850"
43184       ],
43185       [
43186         "Northern Mariana Islands",
43187         "mp",
43188         "1670"
43189       ],
43190       [
43191         "Norway (Norge)",
43192         "no",
43193         "47",
43194         0
43195       ],
43196       [
43197         "Oman (‫عُمان‬‎)",
43198         "om",
43199         "968"
43200       ],
43201       [
43202         "Pakistan (‫پاکستان‬‎)",
43203         "pk",
43204         "92"
43205       ],
43206       [
43207         "Palau",
43208         "pw",
43209         "680"
43210       ],
43211       [
43212         "Palestine (‫فلسطين‬‎)",
43213         "ps",
43214         "970"
43215       ],
43216       [
43217         "Panama (Panamá)",
43218         "pa",
43219         "507"
43220       ],
43221       [
43222         "Papua New Guinea",
43223         "pg",
43224         "675"
43225       ],
43226       [
43227         "Paraguay",
43228         "py",
43229         "595"
43230       ],
43231       [
43232         "Peru (Perú)",
43233         "pe",
43234         "51"
43235       ],
43236       [
43237         "Philippines",
43238         "ph",
43239         "63"
43240       ],
43241       [
43242         "Poland (Polska)",
43243         "pl",
43244         "48"
43245       ],
43246       [
43247         "Portugal",
43248         "pt",
43249         "351"
43250       ],
43251       [
43252         "Puerto Rico",
43253         "pr",
43254         "1",
43255         3,
43256         ["787", "939"]
43257       ],
43258       [
43259         "Qatar (‫قطر‬‎)",
43260         "qa",
43261         "974"
43262       ],
43263       [
43264         "Réunion (La Réunion)",
43265         "re",
43266         "262",
43267         0
43268       ],
43269       [
43270         "Romania (România)",
43271         "ro",
43272         "40"
43273       ],
43274       [
43275         "Russia (Россия)",
43276         "ru",
43277         "7",
43278         0
43279       ],
43280       [
43281         "Rwanda",
43282         "rw",
43283         "250"
43284       ],
43285       [
43286         "Saint Barthélemy",
43287         "bl",
43288         "590",
43289         1
43290       ],
43291       [
43292         "Saint Helena",
43293         "sh",
43294         "290"
43295       ],
43296       [
43297         "Saint Kitts and Nevis",
43298         "kn",
43299         "1869"
43300       ],
43301       [
43302         "Saint Lucia",
43303         "lc",
43304         "1758"
43305       ],
43306       [
43307         "Saint Martin (Saint-Martin (partie française))",
43308         "mf",
43309         "590",
43310         2
43311       ],
43312       [
43313         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43314         "pm",
43315         "508"
43316       ],
43317       [
43318         "Saint Vincent and the Grenadines",
43319         "vc",
43320         "1784"
43321       ],
43322       [
43323         "Samoa",
43324         "ws",
43325         "685"
43326       ],
43327       [
43328         "San Marino",
43329         "sm",
43330         "378"
43331       ],
43332       [
43333         "São Tomé and Príncipe (São Tomé e Príncipe)",
43334         "st",
43335         "239"
43336       ],
43337       [
43338         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43339         "sa",
43340         "966"
43341       ],
43342       [
43343         "Senegal (Sénégal)",
43344         "sn",
43345         "221"
43346       ],
43347       [
43348         "Serbia (Србија)",
43349         "rs",
43350         "381"
43351       ],
43352       [
43353         "Seychelles",
43354         "sc",
43355         "248"
43356       ],
43357       [
43358         "Sierra Leone",
43359         "sl",
43360         "232"
43361       ],
43362       [
43363         "Singapore",
43364         "sg",
43365         "65"
43366       ],
43367       [
43368         "Sint Maarten",
43369         "sx",
43370         "1721"
43371       ],
43372       [
43373         "Slovakia (Slovensko)",
43374         "sk",
43375         "421"
43376       ],
43377       [
43378         "Slovenia (Slovenija)",
43379         "si",
43380         "386"
43381       ],
43382       [
43383         "Solomon Islands",
43384         "sb",
43385         "677"
43386       ],
43387       [
43388         "Somalia (Soomaaliya)",
43389         "so",
43390         "252"
43391       ],
43392       [
43393         "South Africa",
43394         "za",
43395         "27"
43396       ],
43397       [
43398         "South Korea (대한민국)",
43399         "kr",
43400         "82"
43401       ],
43402       [
43403         "South Sudan (‫جنوب السودان‬‎)",
43404         "ss",
43405         "211"
43406       ],
43407       [
43408         "Spain (España)",
43409         "es",
43410         "34"
43411       ],
43412       [
43413         "Sri Lanka (ශ්‍රී ලංකාව)",
43414         "lk",
43415         "94"
43416       ],
43417       [
43418         "Sudan (‫السودان‬‎)",
43419         "sd",
43420         "249"
43421       ],
43422       [
43423         "Suriname",
43424         "sr",
43425         "597"
43426       ],
43427       [
43428         "Svalbard and Jan Mayen",
43429         "sj",
43430         "47",
43431         1
43432       ],
43433       [
43434         "Swaziland",
43435         "sz",
43436         "268"
43437       ],
43438       [
43439         "Sweden (Sverige)",
43440         "se",
43441         "46"
43442       ],
43443       [
43444         "Switzerland (Schweiz)",
43445         "ch",
43446         "41"
43447       ],
43448       [
43449         "Syria (‫سوريا‬‎)",
43450         "sy",
43451         "963"
43452       ],
43453       [
43454         "Taiwan (台灣)",
43455         "tw",
43456         "886"
43457       ],
43458       [
43459         "Tajikistan",
43460         "tj",
43461         "992"
43462       ],
43463       [
43464         "Tanzania",
43465         "tz",
43466         "255"
43467       ],
43468       [
43469         "Thailand (ไทย)",
43470         "th",
43471         "66"
43472       ],
43473       [
43474         "Timor-Leste",
43475         "tl",
43476         "670"
43477       ],
43478       [
43479         "Togo",
43480         "tg",
43481         "228"
43482       ],
43483       [
43484         "Tokelau",
43485         "tk",
43486         "690"
43487       ],
43488       [
43489         "Tonga",
43490         "to",
43491         "676"
43492       ],
43493       [
43494         "Trinidad and Tobago",
43495         "tt",
43496         "1868"
43497       ],
43498       [
43499         "Tunisia (‫تونس‬‎)",
43500         "tn",
43501         "216"
43502       ],
43503       [
43504         "Turkey (Türkiye)",
43505         "tr",
43506         "90"
43507       ],
43508       [
43509         "Turkmenistan",
43510         "tm",
43511         "993"
43512       ],
43513       [
43514         "Turks and Caicos Islands",
43515         "tc",
43516         "1649"
43517       ],
43518       [
43519         "Tuvalu",
43520         "tv",
43521         "688"
43522       ],
43523       [
43524         "U.S. Virgin Islands",
43525         "vi",
43526         "1340"
43527       ],
43528       [
43529         "Uganda",
43530         "ug",
43531         "256"
43532       ],
43533       [
43534         "Ukraine (Україна)",
43535         "ua",
43536         "380"
43537       ],
43538       [
43539         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43540         "ae",
43541         "971"
43542       ],
43543       [
43544         "United Kingdom",
43545         "gb",
43546         "44",
43547         0
43548       ],
43549       [
43550         "United States",
43551         "us",
43552         "1",
43553         0
43554       ],
43555       [
43556         "Uruguay",
43557         "uy",
43558         "598"
43559       ],
43560       [
43561         "Uzbekistan (Oʻzbekiston)",
43562         "uz",
43563         "998"
43564       ],
43565       [
43566         "Vanuatu",
43567         "vu",
43568         "678"
43569       ],
43570       [
43571         "Vatican City (Città del Vaticano)",
43572         "va",
43573         "39",
43574         1
43575       ],
43576       [
43577         "Venezuela",
43578         "ve",
43579         "58"
43580       ],
43581       [
43582         "Vietnam (Việt Nam)",
43583         "vn",
43584         "84"
43585       ],
43586       [
43587         "Wallis and Futuna (Wallis-et-Futuna)",
43588         "wf",
43589         "681"
43590       ],
43591       [
43592         "Western Sahara (‫الصحراء الغربية‬‎)",
43593         "eh",
43594         "212",
43595         1
43596       ],
43597       [
43598         "Yemen (‫اليمن‬‎)",
43599         "ye",
43600         "967"
43601       ],
43602       [
43603         "Zambia",
43604         "zm",
43605         "260"
43606       ],
43607       [
43608         "Zimbabwe",
43609         "zw",
43610         "263"
43611       ],
43612       [
43613         "Åland Islands",
43614         "ax",
43615         "358",
43616         1
43617       ]
43618   ];
43619   
43620   return d;
43621 }/**
43622 *    This script refer to:
43623 *    Title: International Telephone Input
43624 *    Author: Jack O'Connor
43625 *    Code version:  v12.1.12
43626 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43627 **/
43628
43629 /**
43630  * @class Roo.bootstrap.PhoneInput
43631  * @extends Roo.bootstrap.TriggerField
43632  * An input with International dial-code selection
43633  
43634  * @cfg {String} defaultDialCode default '+852'
43635  * @cfg {Array} preferedCountries default []
43636   
43637  * @constructor
43638  * Create a new PhoneInput.
43639  * @param {Object} config Configuration options
43640  */
43641
43642 Roo.bootstrap.PhoneInput = function(config) {
43643     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43644 };
43645
43646 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43647         /**
43648         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43649         */
43650         listWidth: undefined,
43651         
43652         selectedClass: 'active',
43653         
43654         invalidClass : "has-warning",
43655         
43656         validClass: 'has-success',
43657         
43658         allowed: '0123456789',
43659         
43660         max_length: 15,
43661         
43662         /**
43663          * @cfg {String} defaultDialCode The default dial code when initializing the input
43664          */
43665         defaultDialCode: '+852',
43666         
43667         /**
43668          * @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
43669          */
43670         preferedCountries: false,
43671         
43672         getAutoCreate : function()
43673         {
43674             var data = Roo.bootstrap.PhoneInputData();
43675             var align = this.labelAlign || this.parentLabelAlign();
43676             var id = Roo.id();
43677             
43678             this.allCountries = [];
43679             this.dialCodeMapping = [];
43680             
43681             for (var i = 0; i < data.length; i++) {
43682               var c = data[i];
43683               this.allCountries[i] = {
43684                 name: c[0],
43685                 iso2: c[1],
43686                 dialCode: c[2],
43687                 priority: c[3] || 0,
43688                 areaCodes: c[4] || null
43689               };
43690               this.dialCodeMapping[c[2]] = {
43691                   name: c[0],
43692                   iso2: c[1],
43693                   priority: c[3] || 0,
43694                   areaCodes: c[4] || null
43695               };
43696             }
43697             
43698             var cfg = {
43699                 cls: 'form-group',
43700                 cn: []
43701             };
43702             
43703             var input =  {
43704                 tag: 'input',
43705                 id : id,
43706                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43707                 maxlength: this.max_length,
43708                 cls : 'form-control tel-input',
43709                 autocomplete: 'new-password'
43710             };
43711             
43712             var hiddenInput = {
43713                 tag: 'input',
43714                 type: 'hidden',
43715                 cls: 'hidden-tel-input'
43716             };
43717             
43718             if (this.name) {
43719                 hiddenInput.name = this.name;
43720             }
43721             
43722             if (this.disabled) {
43723                 input.disabled = true;
43724             }
43725             
43726             var flag_container = {
43727                 tag: 'div',
43728                 cls: 'flag-box',
43729                 cn: [
43730                     {
43731                         tag: 'div',
43732                         cls: 'flag'
43733                     },
43734                     {
43735                         tag: 'div',
43736                         cls: 'caret'
43737                     }
43738                 ]
43739             };
43740             
43741             var box = {
43742                 tag: 'div',
43743                 cls: this.hasFeedback ? 'has-feedback' : '',
43744                 cn: [
43745                     hiddenInput,
43746                     input,
43747                     {
43748                         tag: 'input',
43749                         cls: 'dial-code-holder',
43750                         disabled: true
43751                     }
43752                 ]
43753             };
43754             
43755             var container = {
43756                 cls: 'roo-select2-container input-group',
43757                 cn: [
43758                     flag_container,
43759                     box
43760                 ]
43761             };
43762             
43763             if (this.fieldLabel.length) {
43764                 var indicator = {
43765                     tag: 'i',
43766                     tooltip: 'This field is required'
43767                 };
43768                 
43769                 var label = {
43770                     tag: 'label',
43771                     'for':  id,
43772                     cls: 'control-label',
43773                     cn: []
43774                 };
43775                 
43776                 var label_text = {
43777                     tag: 'span',
43778                     html: this.fieldLabel
43779                 };
43780                 
43781                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43782                 label.cn = [
43783                     indicator,
43784                     label_text
43785                 ];
43786                 
43787                 if(this.indicatorpos == 'right') {
43788                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43789                     label.cn = [
43790                         label_text,
43791                         indicator
43792                     ];
43793                 }
43794                 
43795                 if(align == 'left') {
43796                     container = {
43797                         tag: 'div',
43798                         cn: [
43799                             container
43800                         ]
43801                     };
43802                     
43803                     if(this.labelWidth > 12){
43804                         label.style = "width: " + this.labelWidth + 'px';
43805                     }
43806                     if(this.labelWidth < 13 && this.labelmd == 0){
43807                         this.labelmd = this.labelWidth;
43808                     }
43809                     if(this.labellg > 0){
43810                         label.cls += ' col-lg-' + this.labellg;
43811                         input.cls += ' col-lg-' + (12 - this.labellg);
43812                     }
43813                     if(this.labelmd > 0){
43814                         label.cls += ' col-md-' + this.labelmd;
43815                         container.cls += ' col-md-' + (12 - this.labelmd);
43816                     }
43817                     if(this.labelsm > 0){
43818                         label.cls += ' col-sm-' + this.labelsm;
43819                         container.cls += ' col-sm-' + (12 - this.labelsm);
43820                     }
43821                     if(this.labelxs > 0){
43822                         label.cls += ' col-xs-' + this.labelxs;
43823                         container.cls += ' col-xs-' + (12 - this.labelxs);
43824                     }
43825                 }
43826             }
43827             
43828             cfg.cn = [
43829                 label,
43830                 container
43831             ];
43832             
43833             var settings = this;
43834             
43835             ['xs','sm','md','lg'].map(function(size){
43836                 if (settings[size]) {
43837                     cfg.cls += ' col-' + size + '-' + settings[size];
43838                 }
43839             });
43840             
43841             this.store = new Roo.data.Store({
43842                 proxy : new Roo.data.MemoryProxy({}),
43843                 reader : new Roo.data.JsonReader({
43844                     fields : [
43845                         {
43846                             'name' : 'name',
43847                             'type' : 'string'
43848                         },
43849                         {
43850                             'name' : 'iso2',
43851                             'type' : 'string'
43852                         },
43853                         {
43854                             'name' : 'dialCode',
43855                             'type' : 'string'
43856                         },
43857                         {
43858                             'name' : 'priority',
43859                             'type' : 'string'
43860                         },
43861                         {
43862                             'name' : 'areaCodes',
43863                             'type' : 'string'
43864                         }
43865                     ]
43866                 })
43867             });
43868             
43869             if(!this.preferedCountries) {
43870                 this.preferedCountries = [
43871                     'hk',
43872                     'gb',
43873                     'us'
43874                 ];
43875             }
43876             
43877             var p = this.preferedCountries.reverse();
43878             
43879             if(p) {
43880                 for (var i = 0; i < p.length; i++) {
43881                     for (var j = 0; j < this.allCountries.length; j++) {
43882                         if(this.allCountries[j].iso2 == p[i]) {
43883                             var t = this.allCountries[j];
43884                             this.allCountries.splice(j,1);
43885                             this.allCountries.unshift(t);
43886                         }
43887                     } 
43888                 }
43889             }
43890             
43891             this.store.proxy.data = {
43892                 success: true,
43893                 data: this.allCountries
43894             };
43895             
43896             return cfg;
43897         },
43898         
43899         initEvents : function()
43900         {
43901             this.createList();
43902             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43903             
43904             this.indicator = this.indicatorEl();
43905             this.flag = this.flagEl();
43906             this.dialCodeHolder = this.dialCodeHolderEl();
43907             
43908             this.trigger = this.el.select('div.flag-box',true).first();
43909             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43910             
43911             var _this = this;
43912             
43913             (function(){
43914                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43915                 _this.list.setWidth(lw);
43916             }).defer(100);
43917             
43918             this.list.on('mouseover', this.onViewOver, this);
43919             this.list.on('mousemove', this.onViewMove, this);
43920             this.inputEl().on("keyup", this.onKeyUp, this);
43921             this.inputEl().on("keypress", this.onKeyPress, this);
43922             
43923             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43924
43925             this.view = new Roo.View(this.list, this.tpl, {
43926                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43927             });
43928             
43929             this.view.on('click', this.onViewClick, this);
43930             this.setValue(this.defaultDialCode);
43931         },
43932         
43933         onTriggerClick : function(e)
43934         {
43935             Roo.log('trigger click');
43936             if(this.disabled){
43937                 return;
43938             }
43939             
43940             if(this.isExpanded()){
43941                 this.collapse();
43942                 this.hasFocus = false;
43943             }else {
43944                 this.store.load({});
43945                 this.hasFocus = true;
43946                 this.expand();
43947             }
43948         },
43949         
43950         isExpanded : function()
43951         {
43952             return this.list.isVisible();
43953         },
43954         
43955         collapse : function()
43956         {
43957             if(!this.isExpanded()){
43958                 return;
43959             }
43960             this.list.hide();
43961             Roo.get(document).un('mousedown', this.collapseIf, this);
43962             Roo.get(document).un('mousewheel', this.collapseIf, this);
43963             this.fireEvent('collapse', this);
43964             this.validate();
43965         },
43966         
43967         expand : function()
43968         {
43969             Roo.log('expand');
43970
43971             if(this.isExpanded() || !this.hasFocus){
43972                 return;
43973             }
43974             
43975             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43976             this.list.setWidth(lw);
43977             
43978             this.list.show();
43979             this.restrictHeight();
43980             
43981             Roo.get(document).on('mousedown', this.collapseIf, this);
43982             Roo.get(document).on('mousewheel', this.collapseIf, this);
43983             
43984             this.fireEvent('expand', this);
43985         },
43986         
43987         restrictHeight : function()
43988         {
43989             this.list.alignTo(this.inputEl(), this.listAlign);
43990             this.list.alignTo(this.inputEl(), this.listAlign);
43991         },
43992         
43993         onViewOver : function(e, t)
43994         {
43995             if(this.inKeyMode){
43996                 return;
43997             }
43998             var item = this.view.findItemFromChild(t);
43999             
44000             if(item){
44001                 var index = this.view.indexOf(item);
44002                 this.select(index, false);
44003             }
44004         },
44005
44006         // private
44007         onViewClick : function(view, doFocus, el, e)
44008         {
44009             var index = this.view.getSelectedIndexes()[0];
44010             
44011             var r = this.store.getAt(index);
44012             
44013             if(r){
44014                 this.onSelect(r, index);
44015             }
44016             if(doFocus !== false && !this.blockFocus){
44017                 this.inputEl().focus();
44018             }
44019         },
44020         
44021         onViewMove : function(e, t)
44022         {
44023             this.inKeyMode = false;
44024         },
44025         
44026         select : function(index, scrollIntoView)
44027         {
44028             this.selectedIndex = index;
44029             this.view.select(index);
44030             if(scrollIntoView !== false){
44031                 var el = this.view.getNode(index);
44032                 if(el){
44033                     this.list.scrollChildIntoView(el, false);
44034                 }
44035             }
44036         },
44037         
44038         createList : function()
44039         {
44040             this.list = Roo.get(document.body).createChild({
44041                 tag: 'ul',
44042                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44043                 style: 'display:none'
44044             });
44045             
44046             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44047         },
44048         
44049         collapseIf : function(e)
44050         {
44051             var in_combo  = e.within(this.el);
44052             var in_list =  e.within(this.list);
44053             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44054             
44055             if (in_combo || in_list || is_list) {
44056                 return;
44057             }
44058             this.collapse();
44059         },
44060         
44061         onSelect : function(record, index)
44062         {
44063             if(this.fireEvent('beforeselect', this, record, index) !== false){
44064                 
44065                 this.setFlagClass(record.data.iso2);
44066                 this.setDialCode(record.data.dialCode);
44067                 this.hasFocus = false;
44068                 this.collapse();
44069                 this.fireEvent('select', this, record, index);
44070             }
44071         },
44072         
44073         flagEl : function()
44074         {
44075             var flag = this.el.select('div.flag',true).first();
44076             if(!flag){
44077                 return false;
44078             }
44079             return flag;
44080         },
44081         
44082         dialCodeHolderEl : function()
44083         {
44084             var d = this.el.select('input.dial-code-holder',true).first();
44085             if(!d){
44086                 return false;
44087             }
44088             return d;
44089         },
44090         
44091         setDialCode : function(v)
44092         {
44093             this.dialCodeHolder.dom.value = '+'+v;
44094         },
44095         
44096         setFlagClass : function(n)
44097         {
44098             this.flag.dom.className = 'flag '+n;
44099         },
44100         
44101         getValue : function()
44102         {
44103             var v = this.inputEl().getValue();
44104             if(this.dialCodeHolder) {
44105                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44106             }
44107             return v;
44108         },
44109         
44110         setValue : function(v)
44111         {
44112             var d = this.getDialCode(v);
44113             
44114             //invalid dial code
44115             if(v.length == 0 || !d || d.length == 0) {
44116                 if(this.rendered){
44117                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44118                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44119                 }
44120                 return;
44121             }
44122             
44123             //valid dial code
44124             this.setFlagClass(this.dialCodeMapping[d].iso2);
44125             this.setDialCode(d);
44126             this.inputEl().dom.value = v.replace('+'+d,'');
44127             this.hiddenEl().dom.value = this.getValue();
44128             
44129             this.validate();
44130         },
44131         
44132         getDialCode : function(v)
44133         {
44134             v = v ||  '';
44135             
44136             if (v.length == 0) {
44137                 return this.dialCodeHolder.dom.value;
44138             }
44139             
44140             var dialCode = "";
44141             if (v.charAt(0) != "+") {
44142                 return false;
44143             }
44144             var numericChars = "";
44145             for (var i = 1; i < v.length; i++) {
44146               var c = v.charAt(i);
44147               if (!isNaN(c)) {
44148                 numericChars += c;
44149                 if (this.dialCodeMapping[numericChars]) {
44150                   dialCode = v.substr(1, i);
44151                 }
44152                 if (numericChars.length == 4) {
44153                   break;
44154                 }
44155               }
44156             }
44157             return dialCode;
44158         },
44159         
44160         reset : function()
44161         {
44162             this.setValue(this.defaultDialCode);
44163             this.validate();
44164         },
44165         
44166         hiddenEl : function()
44167         {
44168             return this.el.select('input.hidden-tel-input',true).first();
44169         },
44170         
44171         // after setting val
44172         onKeyUp : function(e){
44173             this.setValue(this.getValue());
44174         },
44175         
44176         onKeyPress : function(e){
44177             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44178                 e.stopEvent();
44179             }
44180         }
44181         
44182 });
44183 /**
44184  * @class Roo.bootstrap.MoneyField
44185  * @extends Roo.bootstrap.ComboBox
44186  * Bootstrap MoneyField class
44187  * 
44188  * @constructor
44189  * Create a new MoneyField.
44190  * @param {Object} config Configuration options
44191  */
44192
44193 Roo.bootstrap.MoneyField = function(config) {
44194     
44195     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44196     
44197 };
44198
44199 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44200     
44201     /**
44202      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44203      */
44204     allowDecimals : true,
44205     /**
44206      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44207      */
44208     decimalSeparator : ".",
44209     /**
44210      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44211      */
44212     decimalPrecision : 0,
44213     /**
44214      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44215      */
44216     allowNegative : true,
44217     /**
44218      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44219      */
44220     allowZero: true,
44221     /**
44222      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44223      */
44224     minValue : Number.NEGATIVE_INFINITY,
44225     /**
44226      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44227      */
44228     maxValue : Number.MAX_VALUE,
44229     /**
44230      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44231      */
44232     minText : "The minimum value for this field is {0}",
44233     /**
44234      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44235      */
44236     maxText : "The maximum value for this field is {0}",
44237     /**
44238      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44239      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44240      */
44241     nanText : "{0} is not a valid number",
44242     /**
44243      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44244      */
44245     castInt : true,
44246     /**
44247      * @cfg {String} defaults currency of the MoneyField
44248      * value should be in lkey
44249      */
44250     defaultCurrency : false,
44251     /**
44252      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44253      */
44254     thousandsDelimiter : false,
44255     /**
44256      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44257      */
44258     max_length: false,
44259     
44260     inputlg : 9,
44261     inputmd : 9,
44262     inputsm : 9,
44263     inputxs : 6,
44264     
44265     store : false,
44266     
44267     getAutoCreate : function()
44268     {
44269         var align = this.labelAlign || this.parentLabelAlign();
44270         
44271         var id = Roo.id();
44272
44273         var cfg = {
44274             cls: 'form-group',
44275             cn: []
44276         };
44277
44278         var input =  {
44279             tag: 'input',
44280             id : id,
44281             cls : 'form-control roo-money-amount-input',
44282             autocomplete: 'new-password'
44283         };
44284         
44285         var hiddenInput = {
44286             tag: 'input',
44287             type: 'hidden',
44288             id: Roo.id(),
44289             cls: 'hidden-number-input'
44290         };
44291         
44292         if(this.max_length) {
44293             input.maxlength = this.max_length; 
44294         }
44295         
44296         if (this.name) {
44297             hiddenInput.name = this.name;
44298         }
44299
44300         if (this.disabled) {
44301             input.disabled = true;
44302         }
44303
44304         var clg = 12 - this.inputlg;
44305         var cmd = 12 - this.inputmd;
44306         var csm = 12 - this.inputsm;
44307         var cxs = 12 - this.inputxs;
44308         
44309         var container = {
44310             tag : 'div',
44311             cls : 'row roo-money-field',
44312             cn : [
44313                 {
44314                     tag : 'div',
44315                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44316                     cn : [
44317                         {
44318                             tag : 'div',
44319                             cls: 'roo-select2-container input-group',
44320                             cn: [
44321                                 {
44322                                     tag : 'input',
44323                                     cls : 'form-control roo-money-currency-input',
44324                                     autocomplete: 'new-password',
44325                                     readOnly : 1,
44326                                     name : this.currencyName
44327                                 },
44328                                 {
44329                                     tag :'span',
44330                                     cls : 'input-group-addon',
44331                                     cn : [
44332                                         {
44333                                             tag: 'span',
44334                                             cls: 'caret'
44335                                         }
44336                                     ]
44337                                 }
44338                             ]
44339                         }
44340                     ]
44341                 },
44342                 {
44343                     tag : 'div',
44344                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44345                     cn : [
44346                         {
44347                             tag: 'div',
44348                             cls: this.hasFeedback ? 'has-feedback' : '',
44349                             cn: [
44350                                 input
44351                             ]
44352                         }
44353                     ]
44354                 }
44355             ]
44356             
44357         };
44358         
44359         if (this.fieldLabel.length) {
44360             var indicator = {
44361                 tag: 'i',
44362                 tooltip: 'This field is required'
44363             };
44364
44365             var label = {
44366                 tag: 'label',
44367                 'for':  id,
44368                 cls: 'control-label',
44369                 cn: []
44370             };
44371
44372             var label_text = {
44373                 tag: 'span',
44374                 html: this.fieldLabel
44375             };
44376
44377             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44378             label.cn = [
44379                 indicator,
44380                 label_text
44381             ];
44382
44383             if(this.indicatorpos == 'right') {
44384                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44385                 label.cn = [
44386                     label_text,
44387                     indicator
44388                 ];
44389             }
44390
44391             if(align == 'left') {
44392                 container = {
44393                     tag: 'div',
44394                     cn: [
44395                         container
44396                     ]
44397                 };
44398
44399                 if(this.labelWidth > 12){
44400                     label.style = "width: " + this.labelWidth + 'px';
44401                 }
44402                 if(this.labelWidth < 13 && this.labelmd == 0){
44403                     this.labelmd = this.labelWidth;
44404                 }
44405                 if(this.labellg > 0){
44406                     label.cls += ' col-lg-' + this.labellg;
44407                     input.cls += ' col-lg-' + (12 - this.labellg);
44408                 }
44409                 if(this.labelmd > 0){
44410                     label.cls += ' col-md-' + this.labelmd;
44411                     container.cls += ' col-md-' + (12 - this.labelmd);
44412                 }
44413                 if(this.labelsm > 0){
44414                     label.cls += ' col-sm-' + this.labelsm;
44415                     container.cls += ' col-sm-' + (12 - this.labelsm);
44416                 }
44417                 if(this.labelxs > 0){
44418                     label.cls += ' col-xs-' + this.labelxs;
44419                     container.cls += ' col-xs-' + (12 - this.labelxs);
44420                 }
44421             }
44422         }
44423
44424         cfg.cn = [
44425             label,
44426             container,
44427             hiddenInput
44428         ];
44429         
44430         var settings = this;
44431
44432         ['xs','sm','md','lg'].map(function(size){
44433             if (settings[size]) {
44434                 cfg.cls += ' col-' + size + '-' + settings[size];
44435             }
44436         });
44437         
44438         return cfg;
44439     },
44440     
44441     initEvents : function()
44442     {
44443         this.indicator = this.indicatorEl();
44444         
44445         this.initCurrencyEvent();
44446         
44447         this.initNumberEvent();
44448     },
44449     
44450     initCurrencyEvent : function()
44451     {
44452         if (!this.store) {
44453             throw "can not find store for combo";
44454         }
44455         
44456         this.store = Roo.factory(this.store, Roo.data);
44457         this.store.parent = this;
44458         
44459         this.createList();
44460         
44461         this.triggerEl = this.el.select('.input-group-addon', true).first();
44462         
44463         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44464         
44465         var _this = this;
44466         
44467         (function(){
44468             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44469             _this.list.setWidth(lw);
44470         }).defer(100);
44471         
44472         this.list.on('mouseover', this.onViewOver, this);
44473         this.list.on('mousemove', this.onViewMove, this);
44474         this.list.on('scroll', this.onViewScroll, this);
44475         
44476         if(!this.tpl){
44477             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44478         }
44479         
44480         this.view = new Roo.View(this.list, this.tpl, {
44481             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44482         });
44483         
44484         this.view.on('click', this.onViewClick, this);
44485         
44486         this.store.on('beforeload', this.onBeforeLoad, this);
44487         this.store.on('load', this.onLoad, this);
44488         this.store.on('loadexception', this.onLoadException, this);
44489         
44490         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44491             "up" : function(e){
44492                 this.inKeyMode = true;
44493                 this.selectPrev();
44494             },
44495
44496             "down" : function(e){
44497                 if(!this.isExpanded()){
44498                     this.onTriggerClick();
44499                 }else{
44500                     this.inKeyMode = true;
44501                     this.selectNext();
44502                 }
44503             },
44504
44505             "enter" : function(e){
44506                 this.collapse();
44507                 
44508                 if(this.fireEvent("specialkey", this, e)){
44509                     this.onViewClick(false);
44510                 }
44511                 
44512                 return true;
44513             },
44514
44515             "esc" : function(e){
44516                 this.collapse();
44517             },
44518
44519             "tab" : function(e){
44520                 this.collapse();
44521                 
44522                 if(this.fireEvent("specialkey", this, e)){
44523                     this.onViewClick(false);
44524                 }
44525                 
44526                 return true;
44527             },
44528
44529             scope : this,
44530
44531             doRelay : function(foo, bar, hname){
44532                 if(hname == 'down' || this.scope.isExpanded()){
44533                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44534                 }
44535                 return true;
44536             },
44537
44538             forceKeyDown: true
44539         });
44540         
44541         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44542         
44543     },
44544     
44545     initNumberEvent : function(e)
44546     {
44547         this.inputEl().on("keydown" , this.fireKey,  this);
44548         this.inputEl().on("focus", this.onFocus,  this);
44549         this.inputEl().on("blur", this.onBlur,  this);
44550         
44551         this.inputEl().relayEvent('keyup', this);
44552         
44553         if(this.indicator){
44554             this.indicator.addClass('invisible');
44555         }
44556  
44557         this.originalValue = this.getValue();
44558         
44559         if(this.validationEvent == 'keyup'){
44560             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44561             this.inputEl().on('keyup', this.filterValidation, this);
44562         }
44563         else if(this.validationEvent !== false){
44564             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44565         }
44566         
44567         if(this.selectOnFocus){
44568             this.on("focus", this.preFocus, this);
44569             
44570         }
44571         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44572             this.inputEl().on("keypress", this.filterKeys, this);
44573         } else {
44574             this.inputEl().relayEvent('keypress', this);
44575         }
44576         
44577         var allowed = "0123456789";
44578         
44579         if(this.allowDecimals){
44580             allowed += this.decimalSeparator;
44581         }
44582         
44583         if(this.allowNegative){
44584             allowed += "-";
44585         }
44586         
44587         if(this.thousandsDelimiter) {
44588             allowed += ",";
44589         }
44590         
44591         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44592         
44593         var keyPress = function(e){
44594             
44595             var k = e.getKey();
44596             
44597             var c = e.getCharCode();
44598             
44599             if(
44600                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44601                     allowed.indexOf(String.fromCharCode(c)) === -1
44602             ){
44603                 e.stopEvent();
44604                 return;
44605             }
44606             
44607             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44608                 return;
44609             }
44610             
44611             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44612                 e.stopEvent();
44613             }
44614         };
44615         
44616         this.inputEl().on("keypress", keyPress, this);
44617         
44618     },
44619     
44620     onTriggerClick : function(e)
44621     {   
44622         if(this.disabled){
44623             return;
44624         }
44625         
44626         this.page = 0;
44627         this.loadNext = false;
44628         
44629         if(this.isExpanded()){
44630             this.collapse();
44631             return;
44632         }
44633         
44634         this.hasFocus = true;
44635         
44636         if(this.triggerAction == 'all') {
44637             this.doQuery(this.allQuery, true);
44638             return;
44639         }
44640         
44641         this.doQuery(this.getRawValue());
44642     },
44643     
44644     getCurrency : function()
44645     {   
44646         var v = this.currencyEl().getValue();
44647         
44648         return v;
44649     },
44650     
44651     restrictHeight : function()
44652     {
44653         this.list.alignTo(this.currencyEl(), this.listAlign);
44654         this.list.alignTo(this.currencyEl(), this.listAlign);
44655     },
44656     
44657     onViewClick : function(view, doFocus, el, e)
44658     {
44659         var index = this.view.getSelectedIndexes()[0];
44660         
44661         var r = this.store.getAt(index);
44662         
44663         if(r){
44664             this.onSelect(r, index);
44665         }
44666     },
44667     
44668     onSelect : function(record, index){
44669         
44670         if(this.fireEvent('beforeselect', this, record, index) !== false){
44671         
44672             this.setFromCurrencyData(index > -1 ? record.data : false);
44673             
44674             this.collapse();
44675             
44676             this.fireEvent('select', this, record, index);
44677         }
44678     },
44679     
44680     setFromCurrencyData : function(o)
44681     {
44682         var currency = '';
44683         
44684         this.lastCurrency = o;
44685         
44686         if (this.currencyField) {
44687             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44688         } else {
44689             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44690         }
44691         
44692         this.lastSelectionText = currency;
44693         
44694         //setting default currency
44695         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44696             this.setCurrency(this.defaultCurrency);
44697             return;
44698         }
44699         
44700         this.setCurrency(currency);
44701     },
44702     
44703     setFromData : function(o)
44704     {
44705         var c = {};
44706         
44707         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44708         
44709         this.setFromCurrencyData(c);
44710         
44711         var value = '';
44712         
44713         if (this.name) {
44714             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44715         } else {
44716             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44717         }
44718         
44719         this.setValue(value);
44720         
44721     },
44722     
44723     setCurrency : function(v)
44724     {   
44725         this.currencyValue = v;
44726         
44727         if(this.rendered){
44728             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44729             this.validate();
44730         }
44731     },
44732     
44733     setValue : function(v)
44734     {
44735         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44736         
44737         this.value = v;
44738         
44739         if(this.rendered){
44740             
44741             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44742             
44743             this.inputEl().dom.value = (v == '') ? '' :
44744                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44745             
44746             if(!this.allowZero && v === '0') {
44747                 this.hiddenEl().dom.value = '';
44748                 this.inputEl().dom.value = '';
44749             }
44750             
44751             this.validate();
44752         }
44753     },
44754     
44755     getRawValue : function()
44756     {
44757         var v = this.inputEl().getValue();
44758         
44759         return v;
44760     },
44761     
44762     getValue : function()
44763     {
44764         return this.fixPrecision(this.parseValue(this.getRawValue()));
44765     },
44766     
44767     parseValue : function(value)
44768     {
44769         if(this.thousandsDelimiter) {
44770             value += "";
44771             r = new RegExp(",", "g");
44772             value = value.replace(r, "");
44773         }
44774         
44775         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44776         return isNaN(value) ? '' : value;
44777         
44778     },
44779     
44780     fixPrecision : function(value)
44781     {
44782         if(this.thousandsDelimiter) {
44783             value += "";
44784             r = new RegExp(",", "g");
44785             value = value.replace(r, "");
44786         }
44787         
44788         var nan = isNaN(value);
44789         
44790         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44791             return nan ? '' : value;
44792         }
44793         return parseFloat(value).toFixed(this.decimalPrecision);
44794     },
44795     
44796     decimalPrecisionFcn : function(v)
44797     {
44798         return Math.floor(v);
44799     },
44800     
44801     validateValue : function(value)
44802     {
44803         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44804             return false;
44805         }
44806         
44807         var num = this.parseValue(value);
44808         
44809         if(isNaN(num)){
44810             this.markInvalid(String.format(this.nanText, value));
44811             return false;
44812         }
44813         
44814         if(num < this.minValue){
44815             this.markInvalid(String.format(this.minText, this.minValue));
44816             return false;
44817         }
44818         
44819         if(num > this.maxValue){
44820             this.markInvalid(String.format(this.maxText, this.maxValue));
44821             return false;
44822         }
44823         
44824         return true;
44825     },
44826     
44827     validate : function()
44828     {
44829         if(this.disabled || this.allowBlank){
44830             this.markValid();
44831             return true;
44832         }
44833         
44834         var currency = this.getCurrency();
44835         
44836         if(this.validateValue(this.getRawValue()) && currency.length){
44837             this.markValid();
44838             return true;
44839         }
44840         
44841         this.markInvalid();
44842         return false;
44843     },
44844     
44845     getName: function()
44846     {
44847         return this.name;
44848     },
44849     
44850     beforeBlur : function()
44851     {
44852         if(!this.castInt){
44853             return;
44854         }
44855         
44856         var v = this.parseValue(this.getRawValue());
44857         
44858         if(v || v == 0){
44859             this.setValue(v);
44860         }
44861     },
44862     
44863     onBlur : function()
44864     {
44865         this.beforeBlur();
44866         
44867         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44868             //this.el.removeClass(this.focusClass);
44869         }
44870         
44871         this.hasFocus = false;
44872         
44873         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44874             this.validate();
44875         }
44876         
44877         var v = this.getValue();
44878         
44879         if(String(v) !== String(this.startValue)){
44880             this.fireEvent('change', this, v, this.startValue);
44881         }
44882         
44883         this.fireEvent("blur", this);
44884     },
44885     
44886     inputEl : function()
44887     {
44888         return this.el.select('.roo-money-amount-input', true).first();
44889     },
44890     
44891     currencyEl : function()
44892     {
44893         return this.el.select('.roo-money-currency-input', true).first();
44894     },
44895     
44896     hiddenEl : function()
44897     {
44898         return this.el.select('input.hidden-number-input',true).first();
44899     }
44900     
44901 });/**
44902  * @class Roo.bootstrap.BezierSignature
44903  * @extends Roo.bootstrap.Component
44904  * Bootstrap BezierSignature class
44905  * This script refer to:
44906  *    Title: Signature Pad
44907  *    Author: szimek
44908  *    Availability: https://github.com/szimek/signature_pad
44909  *
44910  * @constructor
44911  * Create a new BezierSignature
44912  * @param {Object} config The config object
44913  */
44914
44915 Roo.bootstrap.BezierSignature = function(config){
44916     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44917     this.addEvents({
44918         "resize" : true
44919     });
44920 };
44921
44922 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44923 {
44924      
44925     curve_data: [],
44926     
44927     is_empty: true,
44928     
44929     mouse_btn_down: true,
44930     
44931     /**
44932      * @cfg {int} canvas height
44933      */
44934     canvas_height: '200px',
44935     
44936     /**
44937      * @cfg {float|function} Radius of a single dot.
44938      */ 
44939     dot_size: false,
44940     
44941     /**
44942      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44943      */
44944     min_width: 0.5,
44945     
44946     /**
44947      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44948      */
44949     max_width: 2.5,
44950     
44951     /**
44952      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44953      */
44954     throttle: 16,
44955     
44956     /**
44957      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44958      */
44959     min_distance: 5,
44960     
44961     /**
44962      * @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.
44963      */
44964     bg_color: 'rgba(0, 0, 0, 0)',
44965     
44966     /**
44967      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44968      */
44969     dot_color: 'black',
44970     
44971     /**
44972      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44973      */ 
44974     velocity_filter_weight: 0.7,
44975     
44976     /**
44977      * @cfg {function} Callback when stroke begin. 
44978      */
44979     onBegin: false,
44980     
44981     /**
44982      * @cfg {function} Callback when stroke end.
44983      */
44984     onEnd: false,
44985     
44986     getAutoCreate : function()
44987     {
44988         var cls = 'roo-signature column';
44989         
44990         if(this.cls){
44991             cls += ' ' + this.cls;
44992         }
44993         
44994         var col_sizes = [
44995             'lg',
44996             'md',
44997             'sm',
44998             'xs'
44999         ];
45000         
45001         for(var i = 0; i < col_sizes.length; i++) {
45002             if(this[col_sizes[i]]) {
45003                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45004             }
45005         }
45006         
45007         var cfg = {
45008             tag: 'div',
45009             cls: cls,
45010             cn: [
45011                 {
45012                     tag: 'div',
45013                     cls: 'roo-signature-body',
45014                     cn: [
45015                         {
45016                             tag: 'canvas',
45017                             cls: 'roo-signature-body-canvas',
45018                             height: this.canvas_height,
45019                             width: this.canvas_width
45020                         }
45021                     ]
45022                 },
45023                 {
45024                     tag: 'input',
45025                     type: 'file',
45026                     style: 'display: none'
45027                 }
45028             ]
45029         };
45030         
45031         return cfg;
45032     },
45033     
45034     initEvents: function() 
45035     {
45036         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45037         
45038         var canvas = this.canvasEl();
45039         
45040         // mouse && touch event swapping...
45041         canvas.dom.style.touchAction = 'none';
45042         canvas.dom.style.msTouchAction = 'none';
45043         
45044         this.mouse_btn_down = false;
45045         canvas.on('mousedown', this._handleMouseDown, this);
45046         canvas.on('mousemove', this._handleMouseMove, this);
45047         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45048         
45049         if (window.PointerEvent) {
45050             canvas.on('pointerdown', this._handleMouseDown, this);
45051             canvas.on('pointermove', this._handleMouseMove, this);
45052             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45053         }
45054         
45055         if ('ontouchstart' in window) {
45056             canvas.on('touchstart', this._handleTouchStart, this);
45057             canvas.on('touchmove', this._handleTouchMove, this);
45058             canvas.on('touchend', this._handleTouchEnd, this);
45059         }
45060         
45061         Roo.EventManager.onWindowResize(this.resize, this, true);
45062         
45063         // file input event
45064         this.fileEl().on('change', this.uploadImage, this);
45065         
45066         this.clear();
45067         
45068         this.resize();
45069     },
45070     
45071     resize: function(){
45072         
45073         var canvas = this.canvasEl().dom;
45074         var ctx = this.canvasElCtx();
45075         var img_data = false;
45076         
45077         if(canvas.width > 0) {
45078             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45079         }
45080         // setting canvas width will clean img data
45081         canvas.width = 0;
45082         
45083         var style = window.getComputedStyle ? 
45084             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45085             
45086         var padding_left = parseInt(style.paddingLeft) || 0;
45087         var padding_right = parseInt(style.paddingRight) || 0;
45088         
45089         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45090         
45091         if(img_data) {
45092             ctx.putImageData(img_data, 0, 0);
45093         }
45094     },
45095     
45096     _handleMouseDown: function(e)
45097     {
45098         if (e.browserEvent.which === 1) {
45099             this.mouse_btn_down = true;
45100             this.strokeBegin(e);
45101         }
45102     },
45103     
45104     _handleMouseMove: function (e)
45105     {
45106         if (this.mouse_btn_down) {
45107             this.strokeMoveUpdate(e);
45108         }
45109     },
45110     
45111     _handleMouseUp: function (e)
45112     {
45113         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45114             this.mouse_btn_down = false;
45115             this.strokeEnd(e);
45116         }
45117     },
45118     
45119     _handleTouchStart: function (e) {
45120         
45121         e.preventDefault();
45122         if (e.browserEvent.targetTouches.length === 1) {
45123             // var touch = e.browserEvent.changedTouches[0];
45124             // this.strokeBegin(touch);
45125             
45126              this.strokeBegin(e); // assume e catching the correct xy...
45127         }
45128     },
45129     
45130     _handleTouchMove: function (e) {
45131         e.preventDefault();
45132         // var touch = event.targetTouches[0];
45133         // _this._strokeMoveUpdate(touch);
45134         this.strokeMoveUpdate(e);
45135     },
45136     
45137     _handleTouchEnd: function (e) {
45138         var wasCanvasTouched = e.target === this.canvasEl().dom;
45139         if (wasCanvasTouched) {
45140             e.preventDefault();
45141             // var touch = event.changedTouches[0];
45142             // _this._strokeEnd(touch);
45143             this.strokeEnd(e);
45144         }
45145     },
45146     
45147     reset: function () {
45148         this._lastPoints = [];
45149         this._lastVelocity = 0;
45150         this._lastWidth = (this.min_width + this.max_width) / 2;
45151         this.canvasElCtx().fillStyle = this.dot_color;
45152     },
45153     
45154     strokeMoveUpdate: function(e)
45155     {
45156         this.strokeUpdate(e);
45157         
45158         if (this.throttle) {
45159             this.throttleStroke(this.strokeUpdate, this.throttle);
45160         }
45161         else {
45162             this.strokeUpdate(e);
45163         }
45164     },
45165     
45166     strokeBegin: function(e)
45167     {
45168         var newPointGroup = {
45169             color: this.dot_color,
45170             points: []
45171         };
45172         
45173         if (typeof this.onBegin === 'function') {
45174             this.onBegin(e);
45175         }
45176         
45177         this.curve_data.push(newPointGroup);
45178         this.reset();
45179         this.strokeUpdate(e);
45180     },
45181     
45182     strokeUpdate: function(e)
45183     {
45184         var rect = this.canvasEl().dom.getBoundingClientRect();
45185         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45186         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45187         var lastPoints = lastPointGroup.points;
45188         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45189         var isLastPointTooClose = lastPoint
45190             ? point.distanceTo(lastPoint) <= this.min_distance
45191             : false;
45192         var color = lastPointGroup.color;
45193         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45194             var curve = this.addPoint(point);
45195             if (!lastPoint) {
45196                 this.drawDot({color: color, point: point});
45197             }
45198             else if (curve) {
45199                 this.drawCurve({color: color, curve: curve});
45200             }
45201             lastPoints.push({
45202                 time: point.time,
45203                 x: point.x,
45204                 y: point.y
45205             });
45206         }
45207     },
45208     
45209     strokeEnd: function(e)
45210     {
45211         this.strokeUpdate(e);
45212         if (typeof this.onEnd === 'function') {
45213             this.onEnd(e);
45214         }
45215     },
45216     
45217     addPoint:  function (point) {
45218         var _lastPoints = this._lastPoints;
45219         _lastPoints.push(point);
45220         if (_lastPoints.length > 2) {
45221             if (_lastPoints.length === 3) {
45222                 _lastPoints.unshift(_lastPoints[0]);
45223             }
45224             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45225             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45226             _lastPoints.shift();
45227             return curve;
45228         }
45229         return null;
45230     },
45231     
45232     calculateCurveWidths: function (startPoint, endPoint) {
45233         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45234             (1 - this.velocity_filter_weight) * this._lastVelocity;
45235
45236         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45237         var widths = {
45238             end: newWidth,
45239             start: this._lastWidth
45240         };
45241         
45242         this._lastVelocity = velocity;
45243         this._lastWidth = newWidth;
45244         return widths;
45245     },
45246     
45247     drawDot: function (_a) {
45248         var color = _a.color, point = _a.point;
45249         var ctx = this.canvasElCtx();
45250         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45251         ctx.beginPath();
45252         this.drawCurveSegment(point.x, point.y, width);
45253         ctx.closePath();
45254         ctx.fillStyle = color;
45255         ctx.fill();
45256     },
45257     
45258     drawCurve: function (_a) {
45259         var color = _a.color, curve = _a.curve;
45260         var ctx = this.canvasElCtx();
45261         var widthDelta = curve.endWidth - curve.startWidth;
45262         var drawSteps = Math.floor(curve.length()) * 2;
45263         ctx.beginPath();
45264         ctx.fillStyle = color;
45265         for (var i = 0; i < drawSteps; i += 1) {
45266         var t = i / drawSteps;
45267         var tt = t * t;
45268         var ttt = tt * t;
45269         var u = 1 - t;
45270         var uu = u * u;
45271         var uuu = uu * u;
45272         var x = uuu * curve.startPoint.x;
45273         x += 3 * uu * t * curve.control1.x;
45274         x += 3 * u * tt * curve.control2.x;
45275         x += ttt * curve.endPoint.x;
45276         var y = uuu * curve.startPoint.y;
45277         y += 3 * uu * t * curve.control1.y;
45278         y += 3 * u * tt * curve.control2.y;
45279         y += ttt * curve.endPoint.y;
45280         var width = curve.startWidth + ttt * widthDelta;
45281         this.drawCurveSegment(x, y, width);
45282         }
45283         ctx.closePath();
45284         ctx.fill();
45285     },
45286     
45287     drawCurveSegment: function (x, y, width) {
45288         var ctx = this.canvasElCtx();
45289         ctx.moveTo(x, y);
45290         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45291         this.is_empty = false;
45292     },
45293     
45294     clear: function()
45295     {
45296         var ctx = this.canvasElCtx();
45297         var canvas = this.canvasEl().dom;
45298         ctx.fillStyle = this.bg_color;
45299         ctx.clearRect(0, 0, canvas.width, canvas.height);
45300         ctx.fillRect(0, 0, canvas.width, canvas.height);
45301         this.curve_data = [];
45302         this.reset();
45303         this.is_empty = true;
45304     },
45305     
45306     fileEl: function()
45307     {
45308         return  this.el.select('input',true).first();
45309     },
45310     
45311     canvasEl: function()
45312     {
45313         return this.el.select('canvas',true).first();
45314     },
45315     
45316     canvasElCtx: function()
45317     {
45318         return this.el.select('canvas',true).first().dom.getContext('2d');
45319     },
45320     
45321     getImage: function(type)
45322     {
45323         if(this.is_empty) {
45324             return false;
45325         }
45326         
45327         // encryption ?
45328         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45329     },
45330     
45331     drawFromImage: function(img_src)
45332     {
45333         var img = new Image();
45334         
45335         img.onload = function(){
45336             this.canvasElCtx().drawImage(img, 0, 0);
45337         }.bind(this);
45338         
45339         img.src = img_src;
45340         
45341         this.is_empty = false;
45342     },
45343     
45344     selectImage: function()
45345     {
45346         this.fileEl().dom.click();
45347     },
45348     
45349     uploadImage: function(e)
45350     {
45351         var reader = new FileReader();
45352         
45353         reader.onload = function(e){
45354             var img = new Image();
45355             img.onload = function(){
45356                 this.reset();
45357                 this.canvasElCtx().drawImage(img, 0, 0);
45358             }.bind(this);
45359             img.src = e.target.result;
45360         }.bind(this);
45361         
45362         reader.readAsDataURL(e.target.files[0]);
45363     },
45364     
45365     // Bezier Point Constructor
45366     Point: (function () {
45367         function Point(x, y, time) {
45368             this.x = x;
45369             this.y = y;
45370             this.time = time || Date.now();
45371         }
45372         Point.prototype.distanceTo = function (start) {
45373             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45374         };
45375         Point.prototype.equals = function (other) {
45376             return this.x === other.x && this.y === other.y && this.time === other.time;
45377         };
45378         Point.prototype.velocityFrom = function (start) {
45379             return this.time !== start.time
45380             ? this.distanceTo(start) / (this.time - start.time)
45381             : 0;
45382         };
45383         return Point;
45384     }()),
45385     
45386     
45387     // Bezier Constructor
45388     Bezier: (function () {
45389         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45390             this.startPoint = startPoint;
45391             this.control2 = control2;
45392             this.control1 = control1;
45393             this.endPoint = endPoint;
45394             this.startWidth = startWidth;
45395             this.endWidth = endWidth;
45396         }
45397         Bezier.fromPoints = function (points, widths, scope) {
45398             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45399             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45400             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45401         };
45402         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45403             var dx1 = s1.x - s2.x;
45404             var dy1 = s1.y - s2.y;
45405             var dx2 = s2.x - s3.x;
45406             var dy2 = s2.y - s3.y;
45407             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45408             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45409             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45410             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45411             var dxm = m1.x - m2.x;
45412             var dym = m1.y - m2.y;
45413             var k = l2 / (l1 + l2);
45414             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45415             var tx = s2.x - cm.x;
45416             var ty = s2.y - cm.y;
45417             return {
45418                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45419                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45420             };
45421         };
45422         Bezier.prototype.length = function () {
45423             var steps = 10;
45424             var length = 0;
45425             var px;
45426             var py;
45427             for (var i = 0; i <= steps; i += 1) {
45428                 var t = i / steps;
45429                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45430                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45431                 if (i > 0) {
45432                     var xdiff = cx - px;
45433                     var ydiff = cy - py;
45434                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45435                 }
45436                 px = cx;
45437                 py = cy;
45438             }
45439             return length;
45440         };
45441         Bezier.prototype.point = function (t, start, c1, c2, end) {
45442             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45443             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45444             + (3.0 * c2 * (1.0 - t) * t * t)
45445             + (end * t * t * t);
45446         };
45447         return Bezier;
45448     }()),
45449     
45450     throttleStroke: function(fn, wait) {
45451       if (wait === void 0) { wait = 250; }
45452       var previous = 0;
45453       var timeout = null;
45454       var result;
45455       var storedContext;
45456       var storedArgs;
45457       var later = function () {
45458           previous = Date.now();
45459           timeout = null;
45460           result = fn.apply(storedContext, storedArgs);
45461           if (!timeout) {
45462               storedContext = null;
45463               storedArgs = [];
45464           }
45465       };
45466       return function wrapper() {
45467           var args = [];
45468           for (var _i = 0; _i < arguments.length; _i++) {
45469               args[_i] = arguments[_i];
45470           }
45471           var now = Date.now();
45472           var remaining = wait - (now - previous);
45473           storedContext = this;
45474           storedArgs = args;
45475           if (remaining <= 0 || remaining > wait) {
45476               if (timeout) {
45477                   clearTimeout(timeout);
45478                   timeout = null;
45479               }
45480               previous = now;
45481               result = fn.apply(storedContext, storedArgs);
45482               if (!timeout) {
45483                   storedContext = null;
45484                   storedArgs = [];
45485               }
45486           }
45487           else if (!timeout) {
45488               timeout = window.setTimeout(later, remaining);
45489           }
45490           return result;
45491       };
45492   }
45493   
45494 });
45495
45496  
45497
45498