roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * @builder-top
852  * 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  * Bootstrap Container class
1598  * @cfg {Boolean} jumbotron is it a jumbotron element
1599  * @cfg {String} html content of element
1600  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1601  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1602  * @cfg {String} header content of header (for panel)
1603  * @cfg {String} footer content of footer (for panel)
1604  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1605  * @cfg {String} tag (header|aside|section) type of HTML tag.
1606  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1607  * @cfg {String} fa font awesome icon
1608  * @cfg {String} icon (info-sign|check|...) glyphicon name
1609  * @cfg {Boolean} hidden (true|false) hide the element
1610  * @cfg {Boolean} expandable (true|false) default false
1611  * @cfg {Boolean} expanded (true|false) default true
1612  * @cfg {String} rheader contet on the right of header
1613  * @cfg {Boolean} clickable (true|false) default false
1614
1615  *     
1616  * @constructor
1617  * Create a new Container
1618  * @param {Object} config The config object
1619  */
1620
1621 Roo.bootstrap.Container = function(config){
1622     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1623     
1624     this.addEvents({
1625         // raw events
1626          /**
1627          * @event expand
1628          * After the panel has been expand
1629          * 
1630          * @param {Roo.bootstrap.Container} this
1631          */
1632         "expand" : true,
1633         /**
1634          * @event collapse
1635          * After the panel has been collapsed
1636          * 
1637          * @param {Roo.bootstrap.Container} this
1638          */
1639         "collapse" : true,
1640         /**
1641          * @event click
1642          * When a element is chick
1643          * @param {Roo.bootstrap.Container} this
1644          * @param {Roo.EventObject} e
1645          */
1646         "click" : true
1647     });
1648 };
1649
1650 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1651     
1652     jumbotron : false,
1653     well: '',
1654     panel : '',
1655     header: '',
1656     footer : '',
1657     sticky: '',
1658     tag : false,
1659     alert : false,
1660     fa: false,
1661     icon : false,
1662     expandable : false,
1663     rheader : '',
1664     expanded : true,
1665     clickable: false,
1666   
1667      
1668     getChildContainer : function() {
1669         
1670         if(!this.el){
1671             return false;
1672         }
1673         
1674         if (this.panel.length) {
1675             return this.el.select('.panel-body',true).first();
1676         }
1677         
1678         return this.el;
1679     },
1680     
1681     
1682     getAutoCreate : function(){
1683         
1684         var cfg = {
1685             tag : this.tag || 'div',
1686             html : '',
1687             cls : ''
1688         };
1689         if (this.jumbotron) {
1690             cfg.cls = 'jumbotron';
1691         }
1692         
1693         
1694         
1695         // - this is applied by the parent..
1696         //if (this.cls) {
1697         //    cfg.cls = this.cls + '';
1698         //}
1699         
1700         if (this.sticky.length) {
1701             
1702             var bd = Roo.get(document.body);
1703             if (!bd.hasClass('bootstrap-sticky')) {
1704                 bd.addClass('bootstrap-sticky');
1705                 Roo.select('html',true).setStyle('height', '100%');
1706             }
1707              
1708             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1709         }
1710         
1711         
1712         if (this.well.length) {
1713             switch (this.well) {
1714                 case 'lg':
1715                 case 'sm':
1716                     cfg.cls +=' well well-' +this.well;
1717                     break;
1718                 default:
1719                     cfg.cls +=' well';
1720                     break;
1721             }
1722         }
1723         
1724         if (this.hidden) {
1725             cfg.cls += ' hidden';
1726         }
1727         
1728         
1729         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1730             cfg.cls +=' alert alert-' + this.alert;
1731         }
1732         
1733         var body = cfg;
1734         
1735         if (this.panel.length) {
1736             cfg.cls += ' panel panel-' + this.panel;
1737             cfg.cn = [];
1738             if (this.header.length) {
1739                 
1740                 var h = [];
1741                 
1742                 if(this.expandable){
1743                     
1744                     cfg.cls = cfg.cls + ' expandable';
1745                     
1746                     h.push({
1747                         tag: 'i',
1748                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1749                     });
1750                     
1751                 }
1752                 
1753                 h.push(
1754                     {
1755                         tag: 'span',
1756                         cls : 'panel-title',
1757                         html : (this.expandable ? '&nbsp;' : '') + this.header
1758                     },
1759                     {
1760                         tag: 'span',
1761                         cls: 'panel-header-right',
1762                         html: this.rheader
1763                     }
1764                 );
1765                 
1766                 cfg.cn.push({
1767                     cls : 'panel-heading',
1768                     style : this.expandable ? 'cursor: pointer' : '',
1769                     cn : h
1770                 });
1771                 
1772             }
1773             
1774             body = false;
1775             cfg.cn.push({
1776                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1777                 html : this.html
1778             });
1779             
1780             
1781             if (this.footer.length) {
1782                 cfg.cn.push({
1783                     cls : 'panel-footer',
1784                     html : this.footer
1785                     
1786                 });
1787             }
1788             
1789         }
1790         
1791         if (body) {
1792             body.html = this.html || cfg.html;
1793             // prefix with the icons..
1794             if (this.fa) {
1795                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796             }
1797             if (this.icon) {
1798                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1799             }
1800             
1801             
1802         }
1803         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1804             cfg.cls =  'container';
1805         }
1806         
1807         return cfg;
1808     },
1809     
1810     initEvents: function() 
1811     {
1812         if(this.expandable){
1813             var headerEl = this.headerEl();
1814         
1815             if(headerEl){
1816                 headerEl.on('click', this.onToggleClick, this);
1817             }
1818         }
1819         
1820         if(this.clickable){
1821             this.el.on('click', this.onClick, this);
1822         }
1823         
1824     },
1825     
1826     onToggleClick : function()
1827     {
1828         var headerEl = this.headerEl();
1829         
1830         if(!headerEl){
1831             return;
1832         }
1833         
1834         if(this.expanded){
1835             this.collapse();
1836             return;
1837         }
1838         
1839         this.expand();
1840     },
1841     
1842     expand : function()
1843     {
1844         if(this.fireEvent('expand', this)) {
1845             
1846             this.expanded = true;
1847             
1848             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1849             
1850             this.el.select('.panel-body',true).first().removeClass('hide');
1851             
1852             var toggleEl = this.toggleEl();
1853
1854             if(!toggleEl){
1855                 return;
1856             }
1857
1858             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1859         }
1860         
1861     },
1862     
1863     collapse : function()
1864     {
1865         if(this.fireEvent('collapse', this)) {
1866             
1867             this.expanded = false;
1868             
1869             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1870             this.el.select('.panel-body',true).first().addClass('hide');
1871         
1872             var toggleEl = this.toggleEl();
1873
1874             if(!toggleEl){
1875                 return;
1876             }
1877
1878             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1879         }
1880     },
1881     
1882     toggleEl : function()
1883     {
1884         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1885             return;
1886         }
1887         
1888         return this.el.select('.panel-heading .fa',true).first();
1889     },
1890     
1891     headerEl : function()
1892     {
1893         if(!this.el || !this.panel.length || !this.header.length){
1894             return;
1895         }
1896         
1897         return this.el.select('.panel-heading',true).first()
1898     },
1899     
1900     bodyEl : function()
1901     {
1902         if(!this.el || !this.panel.length){
1903             return;
1904         }
1905         
1906         return this.el.select('.panel-body',true).first()
1907     },
1908     
1909     titleEl : function()
1910     {
1911         if(!this.el || !this.panel.length || !this.header.length){
1912             return;
1913         }
1914         
1915         return this.el.select('.panel-title',true).first();
1916     },
1917     
1918     setTitle : function(v)
1919     {
1920         var titleEl = this.titleEl();
1921         
1922         if(!titleEl){
1923             return;
1924         }
1925         
1926         titleEl.dom.innerHTML = v;
1927     },
1928     
1929     getTitle : function()
1930     {
1931         
1932         var titleEl = this.titleEl();
1933         
1934         if(!titleEl){
1935             return '';
1936         }
1937         
1938         return titleEl.dom.innerHTML;
1939     },
1940     
1941     setRightTitle : function(v)
1942     {
1943         var t = this.el.select('.panel-header-right',true).first();
1944         
1945         if(!t){
1946             return;
1947         }
1948         
1949         t.dom.innerHTML = v;
1950     },
1951     
1952     onClick : function(e)
1953     {
1954         e.preventDefault();
1955         
1956         this.fireEvent('click', this, e);
1957     }
1958 });
1959
1960  /*
1961  *  - LGPL
1962  *
1963  *  This is BS4's Card element.. - similar to our containers probably..
1964  * 
1965  */
1966 /**
1967  * @class Roo.bootstrap.Card
1968  * @extends Roo.bootstrap.Component
1969  * Bootstrap Card class
1970  *
1971  *
1972  * possible... may not be implemented..
1973  * @cfg {String} header_image  src url of image.
1974  * @cfg {String|Object} header
1975  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1976  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1977  * 
1978  * @cfg {String} title
1979  * @cfg {String} subtitle
1980  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1981  * @cfg {String} footer
1982  
1983  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1984  * 
1985  * @cfg {String} margin (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1991  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1992  *
1993  * @cfg {String} padding (0|1|2|3|4|5)
1994  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1995  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1996  * @cfg {String} padding_left (0|1|2|3|4|5)
1997  * @cfg {String} padding_right (0|1|2|3|4|5)
1998  * @cfg {String} padding_x (0|1|2|3|4|5)
1999  * @cfg {String} padding_y (0|1|2|3|4|5)
2000  *
2001  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2006  
2007  * @config {Boolean} dragable  if this card can be dragged.
2008  * @config {String} drag_group  group for drag
2009  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2010  * @config {String} drop_group  group for drag
2011  * 
2012  * @config {Boolean} collapsable can the body be collapsed.
2013  * @config {Boolean} collapsed is the body collapsed when rendered...
2014  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2015  * @config {Boolean} rotated is the body rotated when rendered...
2016  * 
2017  * @constructor
2018  * Create a new Container
2019  * @param {Object} config The config object
2020  */
2021
2022 Roo.bootstrap.Card = function(config){
2023     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2024     
2025     this.addEvents({
2026          // raw events
2027         /**
2028          * @event drop
2029          * When a element a card is dropped
2030          * @param {Roo.bootstrap.Card} this
2031          *
2032          * 
2033          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2034          * @param {String} position 'above' or 'below'
2035          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2036         
2037          */
2038         'drop' : true,
2039          /**
2040          * @event rotate
2041          * When a element a card is rotate
2042          * @param {Roo.bootstrap.Card} this
2043          * @param {Roo.Element} n the node being dropped?
2044          * @param {Boolean} rotate status
2045          */
2046         'rotate' : true,
2047         /**
2048          * @event cardover
2049          * When a card element is dragged over ready to drop (return false to block dropable)
2050          * @param {Roo.bootstrap.Card} this
2051          * @param {Object} data from dragdrop 
2052          */
2053          'cardover' : true
2054          
2055     });
2056 };
2057
2058
2059 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2060     
2061     
2062     weight : '',
2063     
2064     margin: '', /// may be better in component?
2065     margin_top: '', 
2066     margin_bottom: '', 
2067     margin_left: '',
2068     margin_right: '',
2069     margin_x: '',
2070     margin_y: '',
2071     
2072     padding : '',
2073     padding_top: '', 
2074     padding_bottom: '', 
2075     padding_left: '',
2076     padding_right: '',
2077     padding_x: '',
2078     padding_y: '',
2079     
2080     display: '', 
2081     display_xs: '', 
2082     display_sm: '', 
2083     display_lg: '',
2084     display_xl: '',
2085  
2086     header_image  : '',
2087     header : '',
2088     header_size : 0,
2089     title : '',
2090     subtitle : '',
2091     html : '',
2092     footer: '',
2093
2094     collapsable : false,
2095     collapsed : false,
2096     rotateable : false,
2097     rotated : false,
2098     
2099     dragable : false,
2100     drag_group : false,
2101     dropable : false,
2102     drop_group : false,
2103     childContainer : false,
2104     dropEl : false, /// the dom placeholde element that indicates drop location.
2105     containerEl: false, // body container
2106     bodyEl: false, // card-body
2107     headerContainerEl : false, //
2108     headerEl : false,
2109     header_imageEl : false,
2110     
2111     
2112     layoutCls : function()
2113     {
2114         var cls = '';
2115         var t = this;
2116         Roo.log(this.margin_bottom.length);
2117         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2118             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2119             
2120             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2121                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2122             }
2123             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2124                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2125             }
2126         });
2127         
2128         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2129             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2130                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2131             }
2132         });
2133         
2134         // more generic support?
2135         if (this.hidden) {
2136             cls += ' d-none';
2137         }
2138         
2139         return cls;
2140     },
2141  
2142        // Roo.log("Call onRender: " + this.xtype);
2143         /*  We are looking at something like this.
2144 <div class="card">
2145     <img src="..." class="card-img-top" alt="...">
2146     <div class="card-body">
2147         <h5 class="card-title">Card title</h5>
2148          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2149
2150         >> this bit is really the body...
2151         <div> << we will ad dthis in hopefully it will not break shit.
2152         
2153         ** card text does not actually have any styling...
2154         
2155             <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>
2156         
2157         </div> <<
2158           <a href="#" class="card-link">Card link</a>
2159           
2160     </div>
2161     <div class="card-footer">
2162         <small class="text-muted">Last updated 3 mins ago</small>
2163     </div>
2164 </div>
2165          */
2166     getAutoCreate : function(){
2167         
2168         var cfg = {
2169             tag : 'div',
2170             cls : 'card',
2171             cn : [ ]
2172         };
2173         
2174         if (this.weight.length && this.weight != 'light') {
2175             cfg.cls += ' text-white';
2176         } else {
2177             cfg.cls += ' text-dark'; // need as it's nested..
2178         }
2179         if (this.weight.length) {
2180             cfg.cls += ' bg-' + this.weight;
2181         }
2182         
2183         cfg.cls += ' ' + this.layoutCls(); 
2184         
2185         var hdr = false;
2186         var hdr_ctr = false;
2187         if (this.header.length) {
2188             hdr = {
2189                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2190                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2191                 cn : []
2192             };
2193             cfg.cn.push(hdr);
2194             hdr_ctr = hdr;
2195         } else {
2196             hdr = {
2197                 tag : 'div',
2198                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2199                 cn : []
2200             };
2201             cfg.cn.push(hdr);
2202             hdr_ctr = hdr;
2203         }
2204         if (this.collapsable) {
2205             hdr_ctr = {
2206             tag : 'a',
2207             cls : 'd-block user-select-none',
2208             cn: [
2209                     {
2210                         tag: 'i',
2211                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2212                     }
2213                    
2214                 ]
2215             };
2216             hdr.cn.push(hdr_ctr);
2217         }
2218         
2219         hdr_ctr.cn.push(        {
2220             tag: 'span',
2221             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2222             html : this.header
2223         });
2224         
2225         
2226         if (this.header_image.length) {
2227             cfg.cn.push({
2228                 tag : 'img',
2229                 cls : 'card-img-top',
2230                 src: this.header_image // escape?
2231             });
2232         } else {
2233             cfg.cn.push({
2234                     tag : 'div',
2235                     cls : 'card-img-top d-none' 
2236                 });
2237         }
2238             
2239         var body = {
2240             tag : 'div',
2241             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2242             cn : []
2243         };
2244         var obody = body;
2245         if (this.collapsable || this.rotateable) {
2246             obody = {
2247                 tag: 'div',
2248                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2249                 cn : [  body ]
2250             };
2251         }
2252         
2253         cfg.cn.push(obody);
2254         
2255         if (this.title.length) {
2256             body.cn.push({
2257                 tag : 'div',
2258                 cls : 'card-title',
2259                 src: this.title // escape?
2260             });
2261         }  
2262         
2263         if (this.subtitle.length) {
2264             body.cn.push({
2265                 tag : 'div',
2266                 cls : 'card-title',
2267                 src: this.subtitle // escape?
2268             });
2269         }
2270         
2271         body.cn.push({
2272             tag : 'div',
2273             cls : 'roo-card-body-ctr'
2274         });
2275         
2276         if (this.html.length) {
2277             body.cn.push({
2278                 tag: 'div',
2279                 html : this.html
2280             });
2281         }
2282         // fixme ? handle objects?
2283         
2284         if (this.footer.length) {
2285            
2286             cfg.cn.push({
2287                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2288                 html : this.footer
2289             });
2290             
2291         } else {
2292             cfg.cn.push({cls : 'card-footer d-none'});
2293         }
2294         
2295         // footer...
2296         
2297         return cfg;
2298     },
2299     
2300     
2301     getCardHeader : function()
2302     {
2303         var  ret = this.el.select('.card-header',true).first();
2304         if (ret.hasClass('d-none')) {
2305             ret.removeClass('d-none');
2306         }
2307         
2308         return ret;
2309     },
2310     getCardFooter : function()
2311     {
2312         var  ret = this.el.select('.card-footer',true).first();
2313         if (ret.hasClass('d-none')) {
2314             ret.removeClass('d-none');
2315         }
2316         
2317         return ret;
2318     },
2319     getCardImageTop : function()
2320     {
2321         var  ret = this.header_imageEl;
2322         if (ret.hasClass('d-none')) {
2323             ret.removeClass('d-none');
2324         }
2325             
2326         return ret;
2327     },
2328     
2329     getChildContainer : function()
2330     {
2331         
2332         if(!this.el){
2333             return false;
2334         }
2335         return this.el.select('.roo-card-body-ctr',true).first();    
2336     },
2337     
2338     initEvents: function() 
2339     {
2340         this.bodyEl = this.el.select('.card-body',true).first(); 
2341         this.containerEl = this.getChildContainer();
2342         if(this.dragable){
2343             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2344                     containerScroll: true,
2345                     ddGroup: this.drag_group || 'default_card_drag_group'
2346             });
2347             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2348         }
2349         if (this.dropable) {
2350             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2351                 containerScroll: true,
2352                 ddGroup: this.drop_group || 'default_card_drag_group'
2353             });
2354             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2355             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2356             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2357             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2358             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359         }
2360         
2361         if (this.collapsable) {
2362             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2363         }
2364         if (this.rotateable) {
2365             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2366         }
2367         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2368          
2369         this.footerEl = this.el.select('.card-footer',true).first();
2370         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2371         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2372         this.headerEl = this.el.select('.card-header',true).first();
2373         
2374         if (this.rotated) {
2375             this.el.addClass('roo-card-rotated');
2376             this.fireEvent('rotate', this, true);
2377         }
2378         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2379         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380         
2381     },
2382     getDragData : function(e)
2383     {
2384         var target = this.getEl();
2385         if (target) {
2386             //this.handleSelection(e);
2387             
2388             var dragData = {
2389                 source: this,
2390                 copy: false,
2391                 nodes: this.getEl(),
2392                 records: []
2393             };
2394             
2395             
2396             dragData.ddel = target.dom ;    // the div element
2397             Roo.log(target.getWidth( ));
2398             dragData.ddel.style.width = target.getWidth() + 'px';
2399             
2400             return dragData;
2401         }
2402         return false;
2403     },
2404     /**
2405     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2406     *    whole Element becomes the target, and this causes the drop gesture to append.
2407     *
2408     *    Returns an object:
2409     *     {
2410            
2411            position : 'below' or 'above'
2412            card  : relateive to card OBJECT (or true for no cards listed)
2413            items_n : relative to nth item in list
2414            card_n : relative to  nth card in list
2415     }
2416     *
2417     *    
2418     */
2419     getTargetFromEvent : function(e, dragged_card_el)
2420     {
2421         var target = e.getTarget();
2422         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2423             target = target.parentNode;
2424         }
2425         
2426         var ret = {
2427             position: '',
2428             cards : [],
2429             card_n : -1,
2430             items_n : -1,
2431             card : false 
2432         };
2433         
2434         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2435         // see if target is one of the 'cards'...
2436         
2437         
2438         //Roo.log(this.items.length);
2439         var pos = false;
2440         
2441         var last_card_n = 0;
2442         var cards_len  = 0;
2443         for (var i = 0;i< this.items.length;i++) {
2444             
2445             if (!this.items[i].el.hasClass('card')) {
2446                  continue;
2447             }
2448             pos = this.getDropPoint(e, this.items[i].el.dom);
2449             
2450             cards_len = ret.cards.length;
2451             //Roo.log(this.items[i].el.dom.id);
2452             ret.cards.push(this.items[i]);
2453             last_card_n  = i;
2454             if (ret.card_n < 0 && pos == 'above') {
2455                 ret.position = cards_len > 0 ? 'below' : pos;
2456                 ret.items_n = i > 0 ? i - 1 : 0;
2457                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2458                 ret.card = ret.cards[ret.card_n];
2459             }
2460         }
2461         if (!ret.cards.length) {
2462             ret.card = true;
2463             ret.position = 'below';
2464             ret.items_n;
2465             return ret;
2466         }
2467         // could not find a card.. stick it at the end..
2468         if (ret.card_n < 0) {
2469             ret.card_n = last_card_n;
2470             ret.card = ret.cards[last_card_n];
2471             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2472             ret.position = 'below';
2473         }
2474         
2475         if (this.items[ret.items_n].el == dragged_card_el) {
2476             return false;
2477         }
2478         
2479         if (ret.position == 'below') {
2480             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2481             
2482             if (card_after  && card_after.el == dragged_card_el) {
2483                 return false;
2484             }
2485             return ret;
2486         }
2487         
2488         // its's after ..
2489         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2490         
2491         if (card_before  && card_before.el == dragged_card_el) {
2492             return false;
2493         }
2494         
2495         return ret;
2496     },
2497     
2498     onNodeEnter : function(n, dd, e, data){
2499         return false;
2500     },
2501     onNodeOver : function(n, dd, e, data)
2502     {
2503        
2504         var target_info = this.getTargetFromEvent(e,data.source.el);
2505         if (target_info === false) {
2506             this.dropPlaceHolder('hide');
2507             return false;
2508         }
2509         Roo.log(['getTargetFromEvent', target_info ]);
2510         
2511         
2512         if (this.fireEvent('cardover', this, [ data ]) === false) {
2513             return false;
2514         }
2515         
2516         this.dropPlaceHolder('show', target_info,data);
2517         
2518         return false; 
2519     },
2520     onNodeOut : function(n, dd, e, data){
2521         this.dropPlaceHolder('hide');
2522      
2523     },
2524     onNodeDrop : function(n, dd, e, data)
2525     {
2526         
2527         // call drop - return false if
2528         
2529         // this could actually fail - if the Network drops..
2530         // we will ignore this at present..- client should probably reload
2531         // the whole set of cards if stuff like that fails.
2532         
2533         
2534         var info = this.getTargetFromEvent(e,data.source.el);
2535         if (info === false) {
2536             return false;
2537         }
2538         this.dropPlaceHolder('hide');
2539   
2540           
2541     
2542         this.acceptCard(data.source, info.position, info.card, info.items_n);
2543         return true;
2544          
2545     },
2546     firstChildCard : function()
2547     {
2548         for (var i = 0;i< this.items.length;i++) {
2549             
2550             if (!this.items[i].el.hasClass('card')) {
2551                  continue;
2552             }
2553             return this.items[i];
2554         }
2555         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2556     },
2557     /**
2558      * accept card
2559      *
2560      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2561      */
2562     acceptCard : function(move_card,  position, next_to_card )
2563     {
2564         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2565             return false;
2566         }
2567         
2568         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2569         
2570         move_card.parent().removeCard(move_card);
2571         
2572         
2573         var dom = move_card.el.dom;
2574         dom.style.width = ''; // clear with - which is set by drag.
2575         
2576         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2577             var cardel = next_to_card.el.dom;
2578             
2579             if (position == 'above' ) {
2580                 cardel.parentNode.insertBefore(dom, cardel);
2581             } else if (cardel.nextSibling) {
2582                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2583             } else {
2584                 cardel.parentNode.append(dom);
2585             }
2586         } else {
2587             // card container???
2588             this.containerEl.dom.append(dom);
2589         }
2590         
2591         //FIXME HANDLE card = true 
2592         
2593         // add this to the correct place in items.
2594         
2595         // remove Card from items.
2596         
2597        
2598         if (this.items.length) {
2599             var nitems = [];
2600             //Roo.log([info.items_n, info.position, this.items.length]);
2601             for (var i =0; i < this.items.length; i++) {
2602                 if (i == to_items_n && position == 'above') {
2603                     nitems.push(move_card);
2604                 }
2605                 nitems.push(this.items[i]);
2606                 if (i == to_items_n && position == 'below') {
2607                     nitems.push(move_card);
2608                 }
2609             }
2610             this.items = nitems;
2611             Roo.log(this.items);
2612         } else {
2613             this.items.push(move_card);
2614         }
2615         
2616         move_card.parentId = this.id;
2617         
2618         return true;
2619         
2620         
2621     },
2622     removeCard : function(c)
2623     {
2624         this.items = this.items.filter(function(e) { return e != c });
2625  
2626         var dom = c.el.dom;
2627         dom.parentNode.removeChild(dom);
2628         dom.style.width = ''; // clear with - which is set by drag.
2629         c.parentId = false;
2630         
2631     },
2632     
2633     /**    Decide whether to drop above or below a View node. */
2634     getDropPoint : function(e, n, dd)
2635     {
2636         if (dd) {
2637              return false;
2638         }
2639         if (n == this.containerEl.dom) {
2640             return "above";
2641         }
2642         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2643         var c = t + (b - t) / 2;
2644         var y = Roo.lib.Event.getPageY(e);
2645         if(y <= c) {
2646             return "above";
2647         }else{
2648             return "below";
2649         }
2650     },
2651     onToggleCollapse : function(e)
2652         {
2653         if (this.collapsed) {
2654             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2655             this.collapsableEl.addClass('show');
2656             this.collapsed = false;
2657             return;
2658         }
2659         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2660         this.collapsableEl.removeClass('show');
2661         this.collapsed = true;
2662         
2663     
2664     },
2665     
2666     onToggleRotate : function(e)
2667     {
2668         this.collapsableEl.removeClass('show');
2669         this.footerEl.removeClass('d-none');
2670         this.el.removeClass('roo-card-rotated');
2671         this.el.removeClass('d-none');
2672         if (this.rotated) {
2673             
2674             this.collapsableEl.addClass('show');
2675             this.rotated = false;
2676             this.fireEvent('rotate', this, this.rotated);
2677             return;
2678         }
2679         this.el.addClass('roo-card-rotated');
2680         this.footerEl.addClass('d-none');
2681         this.el.select('.roo-collapsable').removeClass('show');
2682         
2683         this.rotated = true;
2684         this.fireEvent('rotate', this, this.rotated);
2685     
2686     },
2687     
2688     dropPlaceHolder: function (action, info, data)
2689     {
2690         if (this.dropEl === false) {
2691             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2692             cls : 'd-none'
2693             },true);
2694         }
2695         this.dropEl.removeClass(['d-none', 'd-block']);        
2696         if (action == 'hide') {
2697             
2698             this.dropEl.addClass('d-none');
2699             return;
2700         }
2701         // FIXME - info.card == true!!!
2702         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2703         
2704         if (info.card !== true) {
2705             var cardel = info.card.el.dom;
2706             
2707             if (info.position == 'above') {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2709             } else if (cardel.nextSibling) {
2710                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2711             } else {
2712                 cardel.parentNode.append(this.dropEl.dom);
2713             }
2714         } else {
2715             // card container???
2716             this.containerEl.dom.append(this.dropEl.dom);
2717         }
2718         
2719         this.dropEl.addClass('d-block roo-card-dropzone');
2720         
2721         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2722         
2723         
2724     
2725     
2726     
2727     },
2728     setHeaderText: function(html)
2729     {
2730         this.header = html;
2731         if (this.headerContainerEl) {
2732             this.headerContainerEl.dom.innerHTML = html;
2733         }
2734     },
2735     onHeaderImageLoad : function(ev, he)
2736     {
2737         if (!this.header_image_fit_square) {
2738             return;
2739         }
2740         
2741         var hw = he.naturalHeight / he.naturalWidth;
2742         // wide image = < 0
2743         // tall image = > 1
2744         //var w = he.dom.naturalWidth;
2745         var ww = he.width;
2746         he.style.left =  0;
2747         he.style.position =  'relative';
2748         if (hw > 1) {
2749             var nw = (ww * (1/hw));
2750             Roo.get(he).setSize( ww * (1/hw),  ww);
2751             he.style.left =  ((ww - nw)/ 2) + 'px';
2752             he.style.position =  'relative';
2753         }
2754
2755     }
2756
2757     
2758 });
2759
2760 /*
2761  * - LGPL
2762  *
2763  * Card header - holder for the card header elements.
2764  * 
2765  */
2766
2767 /**
2768  * @class Roo.bootstrap.CardHeader
2769  * @extends Roo.bootstrap.Element
2770  * Bootstrap CardHeader class
2771  * @constructor
2772  * Create a new Card Header - that you can embed children into
2773  * @param {Object} config The config object
2774  */
2775
2776 Roo.bootstrap.CardHeader = function(config){
2777     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 };
2779
2780 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2781     
2782     
2783     container_method : 'getCardHeader' 
2784     
2785      
2786     
2787     
2788    
2789 });
2790
2791  
2792
2793  /*
2794  * - LGPL
2795  *
2796  * Card footer - holder for the card footer elements.
2797  * 
2798  */
2799
2800 /**
2801  * @class Roo.bootstrap.CardFooter
2802  * @extends Roo.bootstrap.Element
2803  * Bootstrap CardFooter class
2804  * @constructor
2805  * Create a new Card Footer - that you can embed children into
2806  * @param {Object} config The config object
2807  */
2808
2809 Roo.bootstrap.CardFooter = function(config){
2810     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 };
2812
2813 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2814     
2815     
2816     container_method : 'getCardFooter' 
2817     
2818      
2819     
2820     
2821    
2822 });
2823
2824  
2825
2826  /*
2827  * - LGPL
2828  *
2829  * Card header - holder for the card header elements.
2830  * 
2831  */
2832
2833 /**
2834  * @class Roo.bootstrap.CardImageTop
2835  * @extends Roo.bootstrap.Element
2836  * Bootstrap CardImageTop class
2837  * @constructor
2838  * Create a new Card Image Top container
2839  * @param {Object} config The config object
2840  */
2841
2842 Roo.bootstrap.CardImageTop = function(config){
2843     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 };
2845
2846 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2847     
2848    
2849     container_method : 'getCardImageTop' 
2850     
2851      
2852     
2853    
2854 });
2855
2856  
2857
2858  
2859 /*
2860 * Licence: LGPL
2861 */
2862
2863 /**
2864  * @class Roo.bootstrap.ButtonUploader
2865  * @extends Roo.bootstrap.Button
2866  * Bootstrap Button Uploader class - it's a button which when you add files to it
2867  *
2868  * 
2869  * @cfg {Number} errorTimeout default 3000
2870  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2871  * @cfg {Array}  html The button text.
2872  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2873  *
2874  * @constructor
2875  * Create a new CardUploader
2876  * @param {Object} config The config object
2877  */
2878
2879 Roo.bootstrap.ButtonUploader = function(config){
2880     
2881  
2882     
2883     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2884     
2885      
2886      this.addEvents({
2887          // raw events
2888         /**
2889          * @event beforeselect
2890          * When button is pressed, before show upload files dialog is shown
2891          * @param {Roo.bootstrap.UploaderButton} this
2892          *
2893          */
2894         'beforeselect' : true,
2895          /**
2896          * @event fired when files have been selected, 
2897          * When a the download link is clicked
2898          * @param {Roo.bootstrap.UploaderButton} this
2899          * @param {Array} Array of files that have been uploaded
2900          */
2901         'uploaded' : true
2902         
2903     });
2904 };
2905  
2906 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2907     
2908      
2909     errorTimeout : 3000,
2910      
2911     images : false,
2912    
2913     fileCollection : false,
2914     allowBlank : true,
2915     
2916     multiple : true,
2917     
2918     getAutoCreate : function()
2919     {
2920         var im = {
2921             tag: 'input',
2922             type : 'file',
2923             cls : 'd-none  roo-card-upload-selector' 
2924           
2925         };
2926         if (this.multiple) {
2927             im.multiple = 'multiple';
2928         }
2929         
2930         return  {
2931             cls :'div' ,
2932             cn : [
2933                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2934                 im
2935
2936             ]
2937         };
2938            
2939          
2940     },
2941      
2942    
2943     initEvents : function()
2944     {
2945         
2946         Roo.bootstrap.Button.prototype.initEvents.call(this);
2947         
2948         
2949         
2950         
2951         
2952         this.urlAPI = (window.createObjectURL && window) || 
2953                                 (window.URL && URL.revokeObjectURL && URL) || 
2954                                 (window.webkitURL && webkitURL);
2955                         
2956          
2957          
2958          
2959         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2960         
2961         this.selectorEl.on('change', this.onFileSelected, this);
2962          
2963          
2964        
2965     },
2966     
2967    
2968     onClick : function(e)
2969     {
2970         e.preventDefault();
2971         
2972         if ( this.fireEvent('beforeselect', this) === false) {
2973             return;
2974         }
2975          
2976         this.selectorEl.dom.click();
2977          
2978     },
2979     
2980     onFileSelected : function(e)
2981     {
2982         e.preventDefault();
2983         
2984         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985             return;
2986         }
2987         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2988         this.selectorEl.dom.value  = '';// hopefully reset..
2989         
2990         this.fireEvent('uploaded', this,  files );
2991         
2992     },
2993     
2994        
2995    
2996     
2997     /**
2998      * addCard - add an Attachment to the uploader
2999      * @param data - the data about the image to upload
3000      *
3001      * {
3002           id : 123
3003           title : "Title of file",
3004           is_uploaded : false,
3005           src : "http://.....",
3006           srcfile : { the File upload object },
3007           mimetype : file.type,
3008           preview : false,
3009           is_deleted : 0
3010           .. any other data...
3011         }
3012      *
3013      * 
3014     */
3015      
3016     reset: function()
3017     {
3018          
3019          this.selectorEl
3020     } 
3021     
3022     
3023     
3024     
3025 });
3026  /*
3027  * - LGPL
3028  *
3029  * image
3030  * 
3031  */
3032
3033
3034 /**
3035  * @class Roo.bootstrap.Img
3036  * @extends Roo.bootstrap.Component
3037  * Bootstrap Img class
3038  * @cfg {Boolean} imgResponsive false | true
3039  * @cfg {String} border rounded | circle | thumbnail
3040  * @cfg {String} src image source
3041  * @cfg {String} alt image alternative text
3042  * @cfg {String} href a tag href
3043  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3044  * @cfg {String} xsUrl xs image source
3045  * @cfg {String} smUrl sm image source
3046  * @cfg {String} mdUrl md image source
3047  * @cfg {String} lgUrl lg image source
3048  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049  * 
3050  * @constructor
3051  * Create a new Input
3052  * @param {Object} config The config object
3053  */
3054
3055 Roo.bootstrap.Img = function(config){
3056     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3057     
3058     this.addEvents({
3059         // img events
3060         /**
3061          * @event click
3062          * The img click event for the img.
3063          * @param {Roo.EventObject} e
3064          */
3065         "click" : true,
3066         /**
3067          * @event load
3068          * The when any image loads
3069          * @param {Roo.EventObject} e
3070          */
3071         "load" : true
3072     });
3073 };
3074
3075 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3076     
3077     imgResponsive: true,
3078     border: '',
3079     src: 'about:blank',
3080     href: false,
3081     target: false,
3082     xsUrl: '',
3083     smUrl: '',
3084     mdUrl: '',
3085     lgUrl: '',
3086     backgroundContain : false,
3087
3088     getAutoCreate : function()
3089     {   
3090         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3091             return this.createSingleImg();
3092         }
3093         
3094         var cfg = {
3095             tag: 'div',
3096             cls: 'roo-image-responsive-group',
3097             cn: []
3098         };
3099         var _this = this;
3100         
3101         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3102             
3103             if(!_this[size + 'Url']){
3104                 return;
3105             }
3106             
3107             var img = {
3108                 tag: 'img',
3109                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3110                 html: _this.html || cfg.html,
3111                 src: _this[size + 'Url']
3112             };
3113             
3114             img.cls += ' roo-image-responsive-' + size;
3115             
3116             var s = ['xs', 'sm', 'md', 'lg'];
3117             
3118             s.splice(s.indexOf(size), 1);
3119             
3120             Roo.each(s, function(ss){
3121                 img.cls += ' hidden-' + ss;
3122             });
3123             
3124             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3125                 cfg.cls += ' img-' + _this.border;
3126             }
3127             
3128             if(_this.alt){
3129                 cfg.alt = _this.alt;
3130             }
3131             
3132             if(_this.href){
3133                 var a = {
3134                     tag: 'a',
3135                     href: _this.href,
3136                     cn: [
3137                         img
3138                     ]
3139                 };
3140
3141                 if(this.target){
3142                     a.target = _this.target;
3143                 }
3144             }
3145             
3146             cfg.cn.push((_this.href) ? a : img);
3147             
3148         });
3149         
3150         return cfg;
3151     },
3152     
3153     createSingleImg : function()
3154     {
3155         var cfg = {
3156             tag: 'img',
3157             cls: (this.imgResponsive) ? 'img-responsive' : '',
3158             html : null,
3159             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3160         };
3161         
3162         if (this.backgroundContain) {
3163             cfg.cls += ' background-contain';
3164         }
3165         
3166         cfg.html = this.html || cfg.html;
3167         
3168         if (this.backgroundContain) {
3169             cfg.style="background-image: url(" + this.src + ')';
3170         } else {
3171             cfg.src = this.src || cfg.src;
3172         }
3173         
3174         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3175             cfg.cls += ' img-' + this.border;
3176         }
3177         
3178         if(this.alt){
3179             cfg.alt = this.alt;
3180         }
3181         
3182         if(this.href){
3183             var a = {
3184                 tag: 'a',
3185                 href: this.href,
3186                 cn: [
3187                     cfg
3188                 ]
3189             };
3190             
3191             if(this.target){
3192                 a.target = this.target;
3193             }
3194             
3195         }
3196         
3197         return (this.href) ? a : cfg;
3198     },
3199     
3200     initEvents: function() 
3201     {
3202         if(!this.href){
3203             this.el.on('click', this.onClick, this);
3204         }
3205         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3206             this.el.on('load', this.onImageLoad, this);
3207         } else {
3208             // not sure if this works.. not tested
3209             this.el.select('img', true).on('load', this.onImageLoad, this);
3210         }
3211         
3212     },
3213     
3214     onClick : function(e)
3215     {
3216         Roo.log('img onclick');
3217         this.fireEvent('click', this, e);
3218     },
3219     onImageLoad: function(e)
3220     {
3221         Roo.log('img load');
3222         this.fireEvent('load', this, e);
3223     },
3224     
3225     /**
3226      * Sets the url of the image - used to update it
3227      * @param {String} url the url of the image
3228      */
3229     
3230     setSrc : function(url)
3231     {
3232         this.src =  url;
3233         
3234         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3235             if (this.backgroundContain) {
3236                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3237             } else {
3238                 this.el.dom.src =  url;
3239             }
3240             return;
3241         }
3242         
3243         this.el.select('img', true).first().dom.src =  url;
3244     }
3245     
3246     
3247    
3248 });
3249
3250  /*
3251  * - LGPL
3252  *
3253  * image
3254  * 
3255  */
3256
3257
3258 /**
3259  * @class Roo.bootstrap.Link
3260  * @extends Roo.bootstrap.Component
3261  * Bootstrap Link Class
3262  * @cfg {String} alt image alternative text
3263  * @cfg {String} href a tag href
3264  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3265  * @cfg {String} html the content of the link.
3266  * @cfg {String} anchor name for the anchor link
3267  * @cfg {String} fa - favicon
3268
3269  * @cfg {Boolean} preventDefault (true | false) default false
3270
3271  * 
3272  * @constructor
3273  * Create a new Input
3274  * @param {Object} config The config object
3275  */
3276
3277 Roo.bootstrap.Link = function(config){
3278     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3279     
3280     this.addEvents({
3281         // img events
3282         /**
3283          * @event click
3284          * The img click event for the img.
3285          * @param {Roo.EventObject} e
3286          */
3287         "click" : true
3288     });
3289 };
3290
3291 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3292     
3293     href: false,
3294     target: false,
3295     preventDefault: false,
3296     anchor : false,
3297     alt : false,
3298     fa: false,
3299
3300
3301     getAutoCreate : function()
3302     {
3303         var html = this.html || '';
3304         
3305         if (this.fa !== false) {
3306             html = '<i class="fa fa-' + this.fa + '"></i>';
3307         }
3308         var cfg = {
3309             tag: 'a'
3310         };
3311         // anchor's do not require html/href...
3312         if (this.anchor === false) {
3313             cfg.html = html;
3314             cfg.href = this.href || '#';
3315         } else {
3316             cfg.name = this.anchor;
3317             if (this.html !== false || this.fa !== false) {
3318                 cfg.html = html;
3319             }
3320             if (this.href !== false) {
3321                 cfg.href = this.href;
3322             }
3323         }
3324         
3325         if(this.alt !== false){
3326             cfg.alt = this.alt;
3327         }
3328         
3329         
3330         if(this.target !== false) {
3331             cfg.target = this.target;
3332         }
3333         
3334         return cfg;
3335     },
3336     
3337     initEvents: function() {
3338         
3339         if(!this.href || this.preventDefault){
3340             this.el.on('click', this.onClick, this);
3341         }
3342     },
3343     
3344     onClick : function(e)
3345     {
3346         if(this.preventDefault){
3347             e.preventDefault();
3348         }
3349         //Roo.log('img onclick');
3350         this.fireEvent('click', this, e);
3351     }
3352    
3353 });
3354
3355  /*
3356  * - LGPL
3357  *
3358  * header
3359  * 
3360  */
3361
3362 /**
3363  * @class Roo.bootstrap.Header
3364  * @extends Roo.bootstrap.Component
3365  * Bootstrap Header class
3366  * @cfg {String} html content of header
3367  * @cfg {Number} level (1|2|3|4|5|6) default 1
3368  * 
3369  * @constructor
3370  * Create a new Header
3371  * @param {Object} config The config object
3372  */
3373
3374
3375 Roo.bootstrap.Header  = function(config){
3376     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 };
3378
3379 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3380     
3381     //href : false,
3382     html : false,
3383     level : 1,
3384     
3385     
3386     
3387     getAutoCreate : function(){
3388         
3389         
3390         
3391         var cfg = {
3392             tag: 'h' + (1 *this.level),
3393             html: this.html || ''
3394         } ;
3395         
3396         return cfg;
3397     }
3398    
3399 });
3400
3401  
3402
3403  /*
3404  * Based on:
3405  * Ext JS Library 1.1.1
3406  * Copyright(c) 2006-2007, Ext JS, LLC.
3407  *
3408  * Originally Released Under LGPL - original licence link has changed is not relivant.
3409  *
3410  * Fork - LGPL
3411  * <script type="text/javascript">
3412  */
3413  
3414 /**
3415  * @class Roo.bootstrap.MenuMgr
3416  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417  * @singleton
3418  */
3419 Roo.bootstrap.MenuMgr = function(){
3420    var menus, active, groups = {}, attached = false, lastShow = new Date();
3421
3422    // private - called when first menu is created
3423    function init(){
3424        menus = {};
3425        active = new Roo.util.MixedCollection();
3426        Roo.get(document).addKeyListener(27, function(){
3427            if(active.length > 0){
3428                hideAll();
3429            }
3430        });
3431    }
3432
3433    // private
3434    function hideAll(){
3435        if(active && active.length > 0){
3436            var c = active.clone();
3437            c.each(function(m){
3438                m.hide();
3439            });
3440        }
3441    }
3442
3443    // private
3444    function onHide(m){
3445        active.remove(m);
3446        if(active.length < 1){
3447            Roo.get(document).un("mouseup", onMouseDown);
3448             
3449            attached = false;
3450        }
3451    }
3452
3453    // private
3454    function onShow(m){
3455        var last = active.last();
3456        lastShow = new Date();
3457        active.add(m);
3458        if(!attached){
3459           Roo.get(document).on("mouseup", onMouseDown);
3460            
3461            attached = true;
3462        }
3463        if(m.parentMenu){
3464           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3465           m.parentMenu.activeChild = m;
3466        }else if(last && last.isVisible()){
3467           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3468        }
3469    }
3470
3471    // private
3472    function onBeforeHide(m){
3473        if(m.activeChild){
3474            m.activeChild.hide();
3475        }
3476        if(m.autoHideTimer){
3477            clearTimeout(m.autoHideTimer);
3478            delete m.autoHideTimer;
3479        }
3480    }
3481
3482    // private
3483    function onBeforeShow(m){
3484        var pm = m.parentMenu;
3485        if(!pm && !m.allowOtherMenus){
3486            hideAll();
3487        }else if(pm && pm.activeChild && active != m){
3488            pm.activeChild.hide();
3489        }
3490    }
3491
3492    // private this should really trigger on mouseup..
3493    function onMouseDown(e){
3494         Roo.log("on Mouse Up");
3495         
3496         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3497             Roo.log("MenuManager hideAll");
3498             hideAll();
3499             e.stopEvent();
3500         }
3501         
3502         
3503    }
3504
3505    // private
3506    function onBeforeCheck(mi, state){
3507        if(state){
3508            var g = groups[mi.group];
3509            for(var i = 0, l = g.length; i < l; i++){
3510                if(g[i] != mi){
3511                    g[i].setChecked(false);
3512                }
3513            }
3514        }
3515    }
3516
3517    return {
3518
3519        /**
3520         * Hides all menus that are currently visible
3521         */
3522        hideAll : function(){
3523             hideAll();  
3524        },
3525
3526        // private
3527        register : function(menu){
3528            if(!menus){
3529                init();
3530            }
3531            menus[menu.id] = menu;
3532            menu.on("beforehide", onBeforeHide);
3533            menu.on("hide", onHide);
3534            menu.on("beforeshow", onBeforeShow);
3535            menu.on("show", onShow);
3536            var g = menu.group;
3537            if(g && menu.events["checkchange"]){
3538                if(!groups[g]){
3539                    groups[g] = [];
3540                }
3541                groups[g].push(menu);
3542                menu.on("checkchange", onCheck);
3543            }
3544        },
3545
3546         /**
3547          * Returns a {@link Roo.menu.Menu} object
3548          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3549          * be used to generate and return a new Menu instance.
3550          */
3551        get : function(menu){
3552            if(typeof menu == "string"){ // menu id
3553                return menus[menu];
3554            }else if(menu.events){  // menu instance
3555                return menu;
3556            }
3557            /*else if(typeof menu.length == 'number'){ // array of menu items?
3558                return new Roo.bootstrap.Menu({items:menu});
3559            }else{ // otherwise, must be a config
3560                return new Roo.bootstrap.Menu(menu);
3561            }
3562            */
3563            return false;
3564        },
3565
3566        // private
3567        unregister : function(menu){
3568            delete menus[menu.id];
3569            menu.un("beforehide", onBeforeHide);
3570            menu.un("hide", onHide);
3571            menu.un("beforeshow", onBeforeShow);
3572            menu.un("show", onShow);
3573            var g = menu.group;
3574            if(g && menu.events["checkchange"]){
3575                groups[g].remove(menu);
3576                menu.un("checkchange", onCheck);
3577            }
3578        },
3579
3580        // private
3581        registerCheckable : function(menuItem){
3582            var g = menuItem.group;
3583            if(g){
3584                if(!groups[g]){
3585                    groups[g] = [];
3586                }
3587                groups[g].push(menuItem);
3588                menuItem.on("beforecheckchange", onBeforeCheck);
3589            }
3590        },
3591
3592        // private
3593        unregisterCheckable : function(menuItem){
3594            var g = menuItem.group;
3595            if(g){
3596                groups[g].remove(menuItem);
3597                menuItem.un("beforecheckchange", onBeforeCheck);
3598            }
3599        }
3600    };
3601 }();/*
3602  * - LGPL
3603  *
3604  * menu
3605  * 
3606  */
3607
3608 /**
3609  * @class Roo.bootstrap.Menu
3610  * @extends Roo.bootstrap.Component
3611  * Bootstrap Menu class - container for MenuItems
3612  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3613  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3614  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3615  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3616   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3617   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3618  
3619  * @constructor
3620  * Create a new Menu
3621  * @param {Object} config The config object
3622  */
3623
3624
3625 Roo.bootstrap.Menu = function(config){
3626     
3627     if (config.type == 'treeview') {
3628         // normally menu's are drawn attached to the document to handle layering etc..
3629         // however treeview (used by the docs menu is drawn into the parent element)
3630         this.container_method = 'getChildContainer'; 
3631     }
3632     
3633     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3634     if (this.registerMenu && this.type != 'treeview')  {
3635         Roo.bootstrap.MenuMgr.register(this);
3636     }
3637     
3638     
3639     this.addEvents({
3640         /**
3641          * @event beforeshow
3642          * Fires before this menu is displayed (return false to block)
3643          * @param {Roo.menu.Menu} this
3644          */
3645         beforeshow : true,
3646         /**
3647          * @event beforehide
3648          * Fires before this menu is hidden (return false to block)
3649          * @param {Roo.menu.Menu} this
3650          */
3651         beforehide : true,
3652         /**
3653          * @event show
3654          * Fires after this menu is displayed
3655          * @param {Roo.menu.Menu} this
3656          */
3657         show : true,
3658         /**
3659          * @event hide
3660          * Fires after this menu is hidden
3661          * @param {Roo.menu.Menu} this
3662          */
3663         hide : true,
3664         /**
3665          * @event click
3666          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3667          * @param {Roo.menu.Menu} this
3668          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3669          * @param {Roo.EventObject} e
3670          */
3671         click : true,
3672         /**
3673          * @event mouseover
3674          * Fires when the mouse is hovering over this menu
3675          * @param {Roo.menu.Menu} this
3676          * @param {Roo.EventObject} e
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          */
3679         mouseover : true,
3680         /**
3681          * @event mouseout
3682          * Fires when the mouse exits this menu
3683          * @param {Roo.menu.Menu} this
3684          * @param {Roo.EventObject} e
3685          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3686          */
3687         mouseout : true,
3688         /**
3689          * @event itemclick
3690          * Fires when a menu item contained in this menu is clicked
3691          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3692          * @param {Roo.EventObject} e
3693          */
3694         itemclick: true
3695     });
3696     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 };
3698
3699 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3700     
3701    /// html : false,
3702    
3703     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3704     type: false,
3705     /**
3706      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3707      */
3708     registerMenu : true,
3709     
3710     menuItems :false, // stores the menu items..
3711     
3712     hidden:true,
3713         
3714     parentMenu : false,
3715     
3716     stopEvent : true,
3717     
3718     isLink : false,
3719     
3720     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3721     
3722     hideTrigger : false,
3723     
3724     align : 'tl-bl?',
3725     
3726     
3727     getChildContainer : function() {
3728         return this.el;  
3729     },
3730     
3731     getAutoCreate : function(){
3732          
3733         //if (['right'].indexOf(this.align)!==-1) {
3734         //    cfg.cn[1].cls += ' pull-right'
3735         //}
3736          
3737         var cfg = {
3738             tag : 'ul',
3739             cls : 'dropdown-menu shadow' ,
3740             style : 'z-index:1000'
3741             
3742         };
3743         
3744         if (this.type === 'submenu') {
3745             cfg.cls = 'submenu active';
3746         }
3747         if (this.type === 'treeview') {
3748             cfg.cls = 'treeview-menu';
3749         }
3750         
3751         return cfg;
3752     },
3753     initEvents : function() {
3754         
3755        // Roo.log("ADD event");
3756        // Roo.log(this.triggerEl.dom);
3757         if (this.triggerEl) {
3758             
3759             this.triggerEl.on('click', this.onTriggerClick, this);
3760             
3761             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3762             
3763             if (!this.hideTrigger) {
3764                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3765                     // dropdown toggle on the 'a' in BS4?
3766                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3767                 } else {
3768                     this.triggerEl.addClass('dropdown-toggle');
3769                 }
3770             }
3771         }
3772         
3773         if (Roo.isTouch) {
3774             this.el.on('touchstart'  , this.onTouch, this);
3775         }
3776         this.el.on('click' , this.onClick, this);
3777
3778         this.el.on("mouseover", this.onMouseOver, this);
3779         this.el.on("mouseout", this.onMouseOut, this);
3780         
3781     },
3782     
3783     findTargetItem : function(e)
3784     {
3785         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3786         if(!t){
3787             return false;
3788         }
3789         //Roo.log(t);         Roo.log(t.id);
3790         if(t && t.id){
3791             //Roo.log(this.menuitems);
3792             return this.menuitems.get(t.id);
3793             
3794             //return this.items.get(t.menuItemId);
3795         }
3796         
3797         return false;
3798     },
3799     
3800     onTouch : function(e) 
3801     {
3802         Roo.log("menu.onTouch");
3803         //e.stopEvent(); this make the user popdown broken
3804         this.onClick(e);
3805     },
3806     
3807     onClick : function(e)
3808     {
3809         Roo.log("menu.onClick");
3810         
3811         var t = this.findTargetItem(e);
3812         if(!t || t.isContainer){
3813             return;
3814         }
3815         Roo.log(e);
3816         /*
3817         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3818             if(t == this.activeItem && t.shouldDeactivate(e)){
3819                 this.activeItem.deactivate();
3820                 delete this.activeItem;
3821                 return;
3822             }
3823             if(t.canActivate){
3824                 this.setActiveItem(t, true);
3825             }
3826             return;
3827             
3828             
3829         }
3830         */
3831        
3832         Roo.log('pass click event');
3833         
3834         t.onClick(e);
3835         
3836         this.fireEvent("click", this, t, e);
3837         
3838         var _this = this;
3839         
3840         if(!t.href.length || t.href == '#'){
3841             (function() { _this.hide(); }).defer(100);
3842         }
3843         
3844     },
3845     
3846     onMouseOver : function(e){
3847         var t  = this.findTargetItem(e);
3848         //Roo.log(t);
3849         //if(t){
3850         //    if(t.canActivate && !t.disabled){
3851         //        this.setActiveItem(t, true);
3852         //    }
3853         //}
3854         
3855         this.fireEvent("mouseover", this, e, t);
3856     },
3857     isVisible : function(){
3858         return !this.hidden;
3859     },
3860     onMouseOut : function(e){
3861         var t  = this.findTargetItem(e);
3862         
3863         //if(t ){
3864         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3865         //        this.activeItem.deactivate();
3866         //        delete this.activeItem;
3867         //    }
3868         //}
3869         this.fireEvent("mouseout", this, e, t);
3870     },
3871     
3872     
3873     /**
3874      * Displays this menu relative to another element
3875      * @param {String/HTMLElement/Roo.Element} element The element to align to
3876      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3877      * the element (defaults to this.defaultAlign)
3878      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3879      */
3880     show : function(el, pos, parentMenu)
3881     {
3882         if (false === this.fireEvent("beforeshow", this)) {
3883             Roo.log("show canceled");
3884             return;
3885         }
3886         this.parentMenu = parentMenu;
3887         if(!this.el){
3888             this.render();
3889         }
3890         this.el.addClass('show'); // show otherwise we do not know how big we are..
3891          
3892         var xy = this.el.getAlignToXY(el, pos);
3893         
3894         // bl-tl << left align  below
3895         // tl-bl << left align 
3896         
3897         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3898             // if it goes to far to the right.. -> align left.
3899             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900         }
3901         if(xy[0] < 0){
3902             // was left align - go right?
3903             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904         }
3905         
3906         // goes down the bottom
3907         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3908            xy[1]  < 0 ){
3909             var a = this.align.replace('?', '').split('-');
3910             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3911             
3912         }
3913         
3914         this.showAt(  xy , parentMenu, false);
3915     },
3916      /**
3917      * Displays this menu at a specific xy position
3918      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3919      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3920      */
3921     showAt : function(xy, parentMenu, /* private: */_e){
3922         this.parentMenu = parentMenu;
3923         if(!this.el){
3924             this.render();
3925         }
3926         if(_e !== false){
3927             this.fireEvent("beforeshow", this);
3928             //xy = this.el.adjustForConstraints(xy);
3929         }
3930         
3931         //this.el.show();
3932         this.hideMenuItems();
3933         this.hidden = false;
3934         if (this.triggerEl) {
3935             this.triggerEl.addClass('open');
3936         }
3937         
3938         this.el.addClass('show');
3939         
3940         
3941         
3942         // reassign x when hitting right
3943         
3944         // reassign y when hitting bottom
3945         
3946         // but the list may align on trigger left or trigger top... should it be a properity?
3947         
3948         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3949             this.el.setXY(xy);
3950         }
3951         
3952         this.focus();
3953         this.fireEvent("show", this);
3954     },
3955     
3956     focus : function(){
3957         return;
3958         if(!this.hidden){
3959             this.doFocus.defer(50, this);
3960         }
3961     },
3962
3963     doFocus : function(){
3964         if(!this.hidden){
3965             this.focusEl.focus();
3966         }
3967     },
3968
3969     /**
3970      * Hides this menu and optionally all parent menus
3971      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3972      */
3973     hide : function(deep)
3974     {
3975         if (false === this.fireEvent("beforehide", this)) {
3976             Roo.log("hide canceled");
3977             return;
3978         }
3979         this.hideMenuItems();
3980         if(this.el && this.isVisible()){
3981            
3982             if(this.activeItem){
3983                 this.activeItem.deactivate();
3984                 this.activeItem = null;
3985             }
3986             if (this.triggerEl) {
3987                 this.triggerEl.removeClass('open');
3988             }
3989             
3990             this.el.removeClass('show');
3991             this.hidden = true;
3992             this.fireEvent("hide", this);
3993         }
3994         if(deep === true && this.parentMenu){
3995             this.parentMenu.hide(true);
3996         }
3997     },
3998     
3999     onTriggerClick : function(e)
4000     {
4001         Roo.log('trigger click');
4002         
4003         var target = e.getTarget();
4004         
4005         Roo.log(target.nodeName.toLowerCase());
4006         
4007         if(target.nodeName.toLowerCase() === 'i'){
4008             e.preventDefault();
4009         }
4010         
4011     },
4012     
4013     onTriggerPress  : function(e)
4014     {
4015         Roo.log('trigger press');
4016         //Roo.log(e.getTarget());
4017        // Roo.log(this.triggerEl.dom);
4018        
4019         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4020         var pel = Roo.get(e.getTarget());
4021         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4022             Roo.log('is treeview or dropdown?');
4023             return;
4024         }
4025         
4026         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4027             return;
4028         }
4029         
4030         if (this.isVisible()) {
4031             Roo.log('hide');
4032             this.hide();
4033         } else {
4034             Roo.log('show');
4035             
4036             this.show(this.triggerEl, this.align, false);
4037         }
4038         
4039         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4040             e.stopEvent();
4041         }
4042         
4043     },
4044        
4045     
4046     hideMenuItems : function()
4047     {
4048         Roo.log("hide Menu Items");
4049         if (!this.el) { 
4050             return;
4051         }
4052         
4053         this.el.select('.open',true).each(function(aa) {
4054             
4055             aa.removeClass('open');
4056          
4057         });
4058     },
4059     addxtypeChild : function (tree, cntr) {
4060         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4061           
4062         this.menuitems.add(comp);
4063         return comp;
4064
4065     },
4066     getEl : function()
4067     {
4068         Roo.log(this.el);
4069         return this.el;
4070     },
4071     
4072     clear : function()
4073     {
4074         this.getEl().dom.innerHTML = '';
4075         this.menuitems.clear();
4076     }
4077 });
4078
4079  
4080  /*
4081  * - LGPL
4082  *
4083  * menu item
4084  * 
4085  */
4086
4087
4088 /**
4089  * @class Roo.bootstrap.MenuItem
4090  * @extends Roo.bootstrap.Component
4091  * Bootstrap MenuItem class
4092  * @cfg {String} html the menu label
4093  * @cfg {String} href the link
4094  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4095  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4096  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4097  * @cfg {String} fa favicon to show on left of menu item.
4098  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4099  * 
4100  * 
4101  * @constructor
4102  * Create a new MenuItem
4103  * @param {Object} config The config object
4104  */
4105
4106
4107 Roo.bootstrap.MenuItem = function(config){
4108     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4109     this.addEvents({
4110         // raw events
4111         /**
4112          * @event click
4113          * The raw click event for the entire grid.
4114          * @param {Roo.bootstrap.MenuItem} this
4115          * @param {Roo.EventObject} e
4116          */
4117         "click" : true
4118     });
4119 };
4120
4121 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4122     
4123     href : false,
4124     html : false,
4125     preventDefault: false,
4126     isContainer : false,
4127     active : false,
4128     fa: false,
4129     
4130     getAutoCreate : function(){
4131         
4132         if(this.isContainer){
4133             return {
4134                 tag: 'li',
4135                 cls: 'dropdown-menu-item '
4136             };
4137         }
4138         var ctag = {
4139             tag: 'span',
4140             html: 'Link'
4141         };
4142         
4143         var anc = {
4144             tag : 'a',
4145             cls : 'dropdown-item',
4146             href : '#',
4147             cn : [  ]
4148         };
4149         
4150         if (this.fa !== false) {
4151             anc.cn.push({
4152                 tag : 'i',
4153                 cls : 'fa fa-' + this.fa
4154             });
4155         }
4156         
4157         anc.cn.push(ctag);
4158         
4159         
4160         var cfg= {
4161             tag: 'li',
4162             cls: 'dropdown-menu-item',
4163             cn: [ anc ]
4164         };
4165         if (this.parent().type == 'treeview') {
4166             cfg.cls = 'treeview-menu';
4167         }
4168         if (this.active) {
4169             cfg.cls += ' active';
4170         }
4171         
4172         
4173         
4174         anc.href = this.href || cfg.cn[0].href ;
4175         ctag.html = this.html || cfg.cn[0].html ;
4176         return cfg;
4177     },
4178     
4179     initEvents: function()
4180     {
4181         if (this.parent().type == 'treeview') {
4182             this.el.select('a').on('click', this.onClick, this);
4183         }
4184         
4185         if (this.menu) {
4186             this.menu.parentType = this.xtype;
4187             this.menu.triggerEl = this.el;
4188             this.menu = this.addxtype(Roo.apply({}, this.menu));
4189         }
4190         
4191     },
4192     onClick : function(e)
4193     {
4194         Roo.log('item on click ');
4195         
4196         if(this.preventDefault){
4197             e.preventDefault();
4198         }
4199         //this.parent().hideMenuItems();
4200         
4201         this.fireEvent('click', this, e);
4202     },
4203     getEl : function()
4204     {
4205         return this.el;
4206     } 
4207 });
4208
4209  
4210
4211  /*
4212  * - LGPL
4213  *
4214  * menu separator
4215  * 
4216  */
4217
4218
4219 /**
4220  * @class Roo.bootstrap.MenuSeparator
4221  * @extends Roo.bootstrap.Component
4222  * Bootstrap MenuSeparator class
4223  * 
4224  * @constructor
4225  * Create a new MenuItem
4226  * @param {Object} config The config object
4227  */
4228
4229
4230 Roo.bootstrap.MenuSeparator = function(config){
4231     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 };
4233
4234 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4235     
4236     getAutoCreate : function(){
4237         var cfg = {
4238             cls: 'divider',
4239             tag : 'li'
4240         };
4241         
4242         return cfg;
4243     }
4244    
4245 });
4246
4247  
4248
4249  
4250 /*
4251 * Licence: LGPL
4252 */
4253
4254 /**
4255  * @class Roo.bootstrap.Modal
4256  * @extends Roo.bootstrap.Component
4257  * @builder-top
4258  * Bootstrap Modal class
4259  * @cfg {String} title Title of dialog
4260  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4261  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4262  * @cfg {Boolean} specificTitle default false
4263  * @cfg {Array} buttons Array of buttons or standard button set..
4264  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4265  * @cfg {Boolean} animate default true
4266  * @cfg {Boolean} allow_close default true
4267  * @cfg {Boolean} fitwindow default false
4268  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4269  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4270  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4271  * @cfg {String} size (sm|lg|xl) default empty
4272  * @cfg {Number} max_width set the max width of modal
4273  * @cfg {Boolean} editableTitle can the title be edited
4274
4275  *
4276  *
4277  * @constructor
4278  * Create a new Modal Dialog
4279  * @param {Object} config The config object
4280  */
4281
4282 Roo.bootstrap.Modal = function(config){
4283     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4284     this.addEvents({
4285         // raw events
4286         /**
4287          * @event btnclick
4288          * The raw btnclick event for the button
4289          * @param {Roo.EventObject} e
4290          */
4291         "btnclick" : true,
4292         /**
4293          * @event resize
4294          * Fire when dialog resize
4295          * @param {Roo.bootstrap.Modal} this
4296          * @param {Roo.EventObject} e
4297          */
4298         "resize" : true,
4299         /**
4300          * @event titlechanged
4301          * Fire when the editable title has been changed
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} value
4304          */
4305         "titlechanged" : true 
4306         
4307     });
4308     this.buttons = this.buttons || [];
4309
4310     if (this.tmpl) {
4311         this.tmpl = Roo.factory(this.tmpl);
4312     }
4313
4314 };
4315
4316 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4317
4318     title : 'test dialog',
4319
4320     buttons : false,
4321
4322     // set on load...
4323
4324     html: false,
4325
4326     tmp: false,
4327
4328     specificTitle: false,
4329
4330     buttonPosition: 'right',
4331
4332     allow_close : true,
4333
4334     animate : true,
4335
4336     fitwindow: false,
4337     
4338      // private
4339     dialogEl: false,
4340     bodyEl:  false,
4341     footerEl:  false,
4342     titleEl:  false,
4343     closeEl:  false,
4344
4345     size: '',
4346     
4347     max_width: 0,
4348     
4349     max_height: 0,
4350     
4351     fit_content: false,
4352     editableTitle  : false,
4353
4354     onRender : function(ct, position)
4355     {
4356         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4357
4358         if(!this.el){
4359             var cfg = Roo.apply({},  this.getAutoCreate());
4360             cfg.id = Roo.id();
4361             //if(!cfg.name){
4362             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4363             //}
4364             //if (!cfg.name.length) {
4365             //    delete cfg.name;
4366            // }
4367             if (this.cls) {
4368                 cfg.cls += ' ' + this.cls;
4369             }
4370             if (this.style) {
4371                 cfg.style = this.style;
4372             }
4373             this.el = Roo.get(document.body).createChild(cfg, position);
4374         }
4375         //var type = this.el.dom.type;
4376
4377
4378         if(this.tabIndex !== undefined){
4379             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4380         }
4381
4382         this.dialogEl = this.el.select('.modal-dialog',true).first();
4383         this.bodyEl = this.el.select('.modal-body',true).first();
4384         this.closeEl = this.el.select('.modal-header .close', true).first();
4385         this.headerEl = this.el.select('.modal-header',true).first();
4386         this.titleEl = this.el.select('.modal-title',true).first();
4387         this.footerEl = this.el.select('.modal-footer',true).first();
4388
4389         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4390         
4391         //this.el.addClass("x-dlg-modal");
4392
4393         if (this.buttons.length) {
4394             Roo.each(this.buttons, function(bb) {
4395                 var b = Roo.apply({}, bb);
4396                 b.xns = b.xns || Roo.bootstrap;
4397                 b.xtype = b.xtype || 'Button';
4398                 if (typeof(b.listeners) == 'undefined') {
4399                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4400                 }
4401
4402                 var btn = Roo.factory(b);
4403
4404                 btn.render(this.getButtonContainer());
4405
4406             },this);
4407         }
4408         // render the children.
4409         var nitems = [];
4410
4411         if(typeof(this.items) != 'undefined'){
4412             var items = this.items;
4413             delete this.items;
4414
4415             for(var i =0;i < items.length;i++) {
4416                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417             }
4418         }
4419
4420         this.items = nitems;
4421
4422         // where are these used - they used to be body/close/footer
4423
4424
4425         this.initEvents();
4426         //this.el.addClass([this.fieldClass, this.cls]);
4427
4428     },
4429
4430     getAutoCreate : function()
4431     {
4432         // we will default to modal-body-overflow - might need to remove or make optional later.
4433         var bdy = {
4434                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4435                 html : this.html || ''
4436         };
4437
4438         var title = {
4439             tag: 'h5',
4440             cls : 'modal-title',
4441             html : this.title
4442         };
4443
4444         if(this.specificTitle){ // WTF is this?
4445             title = this.title;
4446         }
4447
4448         var header = [];
4449         if (this.allow_close && Roo.bootstrap.version == 3) {
4450             header.push({
4451                 tag: 'button',
4452                 cls : 'close',
4453                 html : '&times'
4454             });
4455         }
4456
4457         header.push(title);
4458
4459         if (this.editableTitle) {
4460             header.push({
4461                 cls: 'form-control roo-editable-title d-none',
4462                 tag: 'input',
4463                 type: 'text'
4464             });
4465         }
4466         
4467         if (this.allow_close && Roo.bootstrap.version == 4) {
4468             header.push({
4469                 tag: 'button',
4470                 cls : 'close',
4471                 html : '&times'
4472             });
4473         }
4474         
4475         var size = '';
4476
4477         if(this.size.length){
4478             size = 'modal-' + this.size;
4479         }
4480         
4481         var footer = Roo.bootstrap.version == 3 ?
4482             {
4483                 cls : 'modal-footer',
4484                 cn : [
4485                     {
4486                         tag: 'div',
4487                         cls: 'btn-' + this.buttonPosition
4488                     }
4489                 ]
4490
4491             } :
4492             {  // BS4 uses mr-auto on left buttons....
4493                 cls : 'modal-footer'
4494             };
4495
4496             
4497
4498         
4499         
4500         var modal = {
4501             cls: "modal",
4502              cn : [
4503                 {
4504                     cls: "modal-dialog " + size,
4505                     cn : [
4506                         {
4507                             cls : "modal-content",
4508                             cn : [
4509                                 {
4510                                     cls : 'modal-header',
4511                                     cn : header
4512                                 },
4513                                 bdy,
4514                                 footer
4515                             ]
4516
4517                         }
4518                     ]
4519
4520                 }
4521             ]
4522         };
4523
4524         if(this.animate){
4525             modal.cls += ' fade';
4526         }
4527
4528         return modal;
4529
4530     },
4531     getChildContainer : function() {
4532
4533          return this.bodyEl;
4534
4535     },
4536     getButtonContainer : function() {
4537         
4538          return Roo.bootstrap.version == 4 ?
4539             this.el.select('.modal-footer',true).first()
4540             : this.el.select('.modal-footer div',true).first();
4541
4542     },
4543     initEvents : function()
4544     {
4545         if (this.allow_close) {
4546             this.closeEl.on('click', this.hide, this);
4547         }
4548         Roo.EventManager.onWindowResize(this.resize, this, true);
4549         if (this.editableTitle) {
4550             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4551             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4552             this.headerEditEl.on('keyup', function(e) {
4553                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4554                         this.toggleHeaderInput(false)
4555                     }
4556                 }, this);
4557             this.headerEditEl.on('blur', function(e) {
4558                 this.toggleHeaderInput(false)
4559             },this);
4560         }
4561
4562     },
4563   
4564
4565     resize : function()
4566     {
4567         this.maskEl.setSize(
4568             Roo.lib.Dom.getViewWidth(true),
4569             Roo.lib.Dom.getViewHeight(true)
4570         );
4571         
4572         if (this.fitwindow) {
4573             
4574            this.dialogEl.setStyle( { 'max-width' : '100%' });
4575             this.setSize(
4576                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4577                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4578             );
4579             return;
4580         }
4581         
4582         if(this.max_width !== 0) {
4583             
4584             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4585             
4586             if(this.height) {
4587                 this.setSize(w, this.height);
4588                 return;
4589             }
4590             
4591             if(this.max_height) {
4592                 this.setSize(w,Math.min(
4593                     this.max_height,
4594                     Roo.lib.Dom.getViewportHeight(true) - 60
4595                 ));
4596                 
4597                 return;
4598             }
4599             
4600             if(!this.fit_content) {
4601                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602                 return;
4603             }
4604             
4605             this.setSize(w, Math.min(
4606                 60 +
4607                 this.headerEl.getHeight() + 
4608                 this.footerEl.getHeight() + 
4609                 this.getChildHeight(this.bodyEl.dom.childNodes),
4610                 Roo.lib.Dom.getViewportHeight(true) - 60)
4611             );
4612         }
4613         
4614     },
4615
4616     setSize : function(w,h)
4617     {
4618         if (!w && !h) {
4619             return;
4620         }
4621         
4622         this.resizeTo(w,h);
4623     },
4624
4625     show : function() {
4626
4627         if (!this.rendered) {
4628             this.render();
4629         }
4630         this.toggleHeaderInput(false);
4631         //this.el.setStyle('display', 'block');
4632         this.el.removeClass('hideing');
4633         this.el.dom.style.display='block';
4634         
4635         Roo.get(document.body).addClass('modal-open');
4636  
4637         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4638             
4639             (function(){
4640                 this.el.addClass('show');
4641                 this.el.addClass('in');
4642             }).defer(50, this);
4643         }else{
4644             this.el.addClass('show');
4645             this.el.addClass('in');
4646         }
4647
4648         // not sure how we can show data in here..
4649         //if (this.tmpl) {
4650         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4651         //}
4652
4653         Roo.get(document.body).addClass("x-body-masked");
4654         
4655         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4656         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4657         this.maskEl.dom.style.display = 'block';
4658         this.maskEl.addClass('show');
4659         
4660         
4661         this.resize();
4662         
4663         this.fireEvent('show', this);
4664
4665         // set zindex here - otherwise it appears to be ignored...
4666         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4667
4668         (function () {
4669             this.items.forEach( function(e) {
4670                 e.layout ? e.layout() : false;
4671
4672             });
4673         }).defer(100,this);
4674
4675     },
4676     hide : function()
4677     {
4678         if(this.fireEvent("beforehide", this) !== false){
4679             
4680             this.maskEl.removeClass('show');
4681             
4682             this.maskEl.dom.style.display = '';
4683             Roo.get(document.body).removeClass("x-body-masked");
4684             this.el.removeClass('in');
4685             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4686
4687             if(this.animate){ // why
4688                 this.el.addClass('hideing');
4689                 this.el.removeClass('show');
4690                 (function(){
4691                     if (!this.el.hasClass('hideing')) {
4692                         return; // it's been shown again...
4693                     }
4694                     
4695                     this.el.dom.style.display='';
4696
4697                     Roo.get(document.body).removeClass('modal-open');
4698                     this.el.removeClass('hideing');
4699                 }).defer(150,this);
4700                 
4701             }else{
4702                 this.el.removeClass('show');
4703                 this.el.dom.style.display='';
4704                 Roo.get(document.body).removeClass('modal-open');
4705
4706             }
4707             this.fireEvent('hide', this);
4708         }
4709     },
4710     isVisible : function()
4711     {
4712         
4713         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714         
4715     },
4716
4717     addButton : function(str, cb)
4718     {
4719
4720
4721         var b = Roo.apply({}, { html : str } );
4722         b.xns = b.xns || Roo.bootstrap;
4723         b.xtype = b.xtype || 'Button';
4724         if (typeof(b.listeners) == 'undefined') {
4725             b.listeners = { click : cb.createDelegate(this)  };
4726         }
4727
4728         var btn = Roo.factory(b);
4729
4730         btn.render(this.getButtonContainer());
4731
4732         return btn;
4733
4734     },
4735
4736     setDefaultButton : function(btn)
4737     {
4738         //this.el.select('.modal-footer').()
4739     },
4740
4741     resizeTo: function(w,h)
4742     {
4743         this.dialogEl.setWidth(w);
4744         
4745         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4746
4747         this.bodyEl.setHeight(h - diff);
4748         
4749         this.fireEvent('resize', this);
4750     },
4751     
4752     setContentSize  : function(w, h)
4753     {
4754
4755     },
4756     onButtonClick: function(btn,e)
4757     {
4758         //Roo.log([a,b,c]);
4759         this.fireEvent('btnclick', btn.name, e);
4760     },
4761      /**
4762      * Set the title of the Dialog
4763      * @param {String} str new Title
4764      */
4765     setTitle: function(str) {
4766         this.titleEl.dom.innerHTML = str;
4767         this.title = str;
4768     },
4769     /**
4770      * Set the body of the Dialog
4771      * @param {String} str new Title
4772      */
4773     setBody: function(str) {
4774         this.bodyEl.dom.innerHTML = str;
4775     },
4776     /**
4777      * Set the body of the Dialog using the template
4778      * @param {Obj} data - apply this data to the template and replace the body contents.
4779      */
4780     applyBody: function(obj)
4781     {
4782         if (!this.tmpl) {
4783             Roo.log("Error - using apply Body without a template");
4784             //code
4785         }
4786         this.tmpl.overwrite(this.bodyEl, obj);
4787     },
4788     
4789     getChildHeight : function(child_nodes)
4790     {
4791         if(
4792             !child_nodes ||
4793             child_nodes.length == 0
4794         ) {
4795             return 0;
4796         }
4797         
4798         var child_height = 0;
4799         
4800         for(var i = 0; i < child_nodes.length; i++) {
4801             
4802             /*
4803             * for modal with tabs...
4804             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4805                 
4806                 var layout_childs = child_nodes[i].childNodes;
4807                 
4808                 for(var j = 0; j < layout_childs.length; j++) {
4809                     
4810                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4811                         
4812                         var layout_body_childs = layout_childs[j].childNodes;
4813                         
4814                         for(var k = 0; k < layout_body_childs.length; k++) {
4815                             
4816                             if(layout_body_childs[k].classList.contains('navbar')) {
4817                                 child_height += layout_body_childs[k].offsetHeight;
4818                                 continue;
4819                             }
4820                             
4821                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4822                                 
4823                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4824                                 
4825                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4826                                     
4827                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4828                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4829                                         continue;
4830                                     }
4831                                     
4832                                 }
4833                                 
4834                             }
4835                             
4836                         }
4837                     }
4838                 }
4839                 continue;
4840             }
4841             */
4842             
4843             child_height += child_nodes[i].offsetHeight;
4844             // Roo.log(child_nodes[i].offsetHeight);
4845         }
4846         
4847         return child_height;
4848     },
4849     toggleHeaderInput : function(is_edit)
4850     {
4851         if (!this.editableTitle) {
4852             return; // not editable.
4853         }
4854         if (is_edit && this.is_header_editing) {
4855             return; // already editing..
4856         }
4857         if (is_edit) {
4858     
4859             this.headerEditEl.dom.value = this.title;
4860             this.headerEditEl.removeClass('d-none');
4861             this.headerEditEl.dom.focus();
4862             this.titleEl.addClass('d-none');
4863             
4864             this.is_header_editing = true;
4865             return
4866         }
4867         // flip back to not editing.
4868         this.title = this.headerEditEl.dom.value;
4869         this.headerEditEl.addClass('d-none');
4870         this.titleEl.removeClass('d-none');
4871         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4872         this.is_header_editing = false;
4873         this.fireEvent('titlechanged', this, this.title);
4874     
4875             
4876         
4877     }
4878
4879 });
4880
4881
4882 Roo.apply(Roo.bootstrap.Modal,  {
4883     /**
4884          * Button config that displays a single OK button
4885          * @type Object
4886          */
4887         OK :  [{
4888             name : 'ok',
4889             weight : 'primary',
4890             html : 'OK'
4891         }],
4892         /**
4893          * Button config that displays Yes and No buttons
4894          * @type Object
4895          */
4896         YESNO : [
4897             {
4898                 name  : 'no',
4899                 html : 'No'
4900             },
4901             {
4902                 name  :'yes',
4903                 weight : 'primary',
4904                 html : 'Yes'
4905             }
4906         ],
4907
4908         /**
4909          * Button config that displays OK and Cancel buttons
4910          * @type Object
4911          */
4912         OKCANCEL : [
4913             {
4914                name : 'cancel',
4915                 html : 'Cancel'
4916             },
4917             {
4918                 name : 'ok',
4919                 weight : 'primary',
4920                 html : 'OK'
4921             }
4922         ],
4923         /**
4924          * Button config that displays Yes, No and Cancel buttons
4925          * @type Object
4926          */
4927         YESNOCANCEL : [
4928             {
4929                 name : 'yes',
4930                 weight : 'primary',
4931                 html : 'Yes'
4932             },
4933             {
4934                 name : 'no',
4935                 html : 'No'
4936             },
4937             {
4938                 name : 'cancel',
4939                 html : 'Cancel'
4940             }
4941         ],
4942         
4943         zIndex : 10001
4944 });
4945
4946 /*
4947  * - LGPL
4948  *
4949  * messagebox - can be used as a replace
4950  * 
4951  */
4952 /**
4953  * @class Roo.MessageBox
4954  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4955  * Example usage:
4956  *<pre><code>
4957 // Basic alert:
4958 Roo.Msg.alert('Status', 'Changes saved successfully.');
4959
4960 // Prompt for user data:
4961 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4962     if (btn == 'ok'){
4963         // process text value...
4964     }
4965 });
4966
4967 // Show a dialog using config options:
4968 Roo.Msg.show({
4969    title:'Save Changes?',
4970    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4971    buttons: Roo.Msg.YESNOCANCEL,
4972    fn: processResult,
4973    animEl: 'elId'
4974 });
4975 </code></pre>
4976  * @singleton
4977  */
4978 Roo.bootstrap.MessageBox = function(){
4979     var dlg, opt, mask, waitTimer;
4980     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4981     var buttons, activeTextEl, bwidth;
4982
4983     
4984     // private
4985     var handleButton = function(button){
4986         dlg.hide();
4987         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988     };
4989
4990     // private
4991     var handleHide = function(){
4992         if(opt && opt.cls){
4993             dlg.el.removeClass(opt.cls);
4994         }
4995         //if(waitTimer){
4996         //    Roo.TaskMgr.stop(waitTimer);
4997         //    waitTimer = null;
4998         //}
4999     };
5000
5001     // private
5002     var updateButtons = function(b){
5003         var width = 0;
5004         if(!b){
5005             buttons["ok"].hide();
5006             buttons["cancel"].hide();
5007             buttons["yes"].hide();
5008             buttons["no"].hide();
5009             dlg.footerEl.hide();
5010             
5011             return width;
5012         }
5013         dlg.footerEl.show();
5014         for(var k in buttons){
5015             if(typeof buttons[k] != "function"){
5016                 if(b[k]){
5017                     buttons[k].show();
5018                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5019                     width += buttons[k].el.getWidth()+15;
5020                 }else{
5021                     buttons[k].hide();
5022                 }
5023             }
5024         }
5025         return width;
5026     };
5027
5028     // private
5029     var handleEsc = function(d, k, e){
5030         if(opt && opt.closable !== false){
5031             dlg.hide();
5032         }
5033         if(e){
5034             e.stopEvent();
5035         }
5036     };
5037
5038     return {
5039         /**
5040          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5041          * @return {Roo.BasicDialog} The BasicDialog element
5042          */
5043         getDialog : function(){
5044            if(!dlg){
5045                 dlg = new Roo.bootstrap.Modal( {
5046                     //draggable: true,
5047                     //resizable:false,
5048                     //constraintoviewport:false,
5049                     //fixedcenter:true,
5050                     //collapsible : false,
5051                     //shim:true,
5052                     //modal: true,
5053                 //    width: 'auto',
5054                   //  height:100,
5055                     //buttonAlign:"center",
5056                     closeClick : function(){
5057                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5058                             handleButton("no");
5059                         }else{
5060                             handleButton("cancel");
5061                         }
5062                     }
5063                 });
5064                 dlg.render();
5065                 dlg.on("hide", handleHide);
5066                 mask = dlg.mask;
5067                 //dlg.addKeyListener(27, handleEsc);
5068                 buttons = {};
5069                 this.buttons = buttons;
5070                 var bt = this.buttonText;
5071                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5072                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5073                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5074                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5075                 //Roo.log(buttons);
5076                 bodyEl = dlg.bodyEl.createChild({
5077
5078                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5079                         '<textarea class="roo-mb-textarea"></textarea>' +
5080                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5081                 });
5082                 msgEl = bodyEl.dom.firstChild;
5083                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5084                 textboxEl.enableDisplayMode();
5085                 textboxEl.addKeyListener([10,13], function(){
5086                     if(dlg.isVisible() && opt && opt.buttons){
5087                         if(opt.buttons.ok){
5088                             handleButton("ok");
5089                         }else if(opt.buttons.yes){
5090                             handleButton("yes");
5091                         }
5092                     }
5093                 });
5094                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5095                 textareaEl.enableDisplayMode();
5096                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5097                 progressEl.enableDisplayMode();
5098                 
5099                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5100                 var pf = progressEl.dom.firstChild;
5101                 if (pf) {
5102                     pp = Roo.get(pf.firstChild);
5103                     pp.setHeight(pf.offsetHeight);
5104                 }
5105                 
5106             }
5107             return dlg;
5108         },
5109
5110         /**
5111          * Updates the message box body text
5112          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5113          * the XHTML-compliant non-breaking space character '&amp;#160;')
5114          * @return {Roo.MessageBox} This message box
5115          */
5116         updateText : function(text)
5117         {
5118             if(!dlg.isVisible() && !opt.width){
5119                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5120                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5121             }
5122             msgEl.innerHTML = text || '&#160;';
5123       
5124             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5125             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5126             var w = Math.max(
5127                     Math.min(opt.width || cw , this.maxWidth), 
5128                     Math.max(opt.minWidth || this.minWidth, bwidth)
5129             );
5130             if(opt.prompt){
5131                 activeTextEl.setWidth(w);
5132             }
5133             if(dlg.isVisible()){
5134                 dlg.fixedcenter = false;
5135             }
5136             // to big, make it scroll. = But as usual stupid IE does not support
5137             // !important..
5138             
5139             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5140                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5141                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5142             } else {
5143                 bodyEl.dom.style.height = '';
5144                 bodyEl.dom.style.overflowY = '';
5145             }
5146             if (cw > w) {
5147                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5148             } else {
5149                 bodyEl.dom.style.overflowX = '';
5150             }
5151             
5152             dlg.setContentSize(w, bodyEl.getHeight());
5153             if(dlg.isVisible()){
5154                 dlg.fixedcenter = true;
5155             }
5156             return this;
5157         },
5158
5159         /**
5160          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5161          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5162          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5163          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5164          * @return {Roo.MessageBox} This message box
5165          */
5166         updateProgress : function(value, text){
5167             if(text){
5168                 this.updateText(text);
5169             }
5170             
5171             if (pp) { // weird bug on my firefox - for some reason this is not defined
5172                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5173                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5174             }
5175             return this;
5176         },        
5177
5178         /**
5179          * Returns true if the message box is currently displayed
5180          * @return {Boolean} True if the message box is visible, else false
5181          */
5182         isVisible : function(){
5183             return dlg && dlg.isVisible();  
5184         },
5185
5186         /**
5187          * Hides the message box if it is displayed
5188          */
5189         hide : function(){
5190             if(this.isVisible()){
5191                 dlg.hide();
5192             }  
5193         },
5194
5195         /**
5196          * Displays a new message box, or reinitializes an existing message box, based on the config options
5197          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5198          * The following config object properties are supported:
5199          * <pre>
5200 Property    Type             Description
5201 ----------  ---------------  ------------------------------------------------------------------------------------
5202 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5203                                    closes (defaults to undefined)
5204 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5205                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5206 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5207                                    progress and wait dialogs will ignore this property and always hide the
5208                                    close button as they can only be closed programmatically.
5209 cls               String           A custom CSS class to apply to the message box element
5210 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5211                                    displayed (defaults to 75)
5212 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5213                                    function will be btn (the name of the button that was clicked, if applicable,
5214                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5215                                    Progress and wait dialogs will ignore this option since they do not respond to
5216                                    user actions and can only be closed programmatically, so any required function
5217                                    should be called by the same code after it closes the dialog.
5218 icon              String           A CSS class that provides a background image to be used as an icon for
5219                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5220 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5221 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5222 modal             Boolean          False to allow user interaction with the page while the message box is
5223                                    displayed (defaults to true)
5224 msg               String           A string that will replace the existing message box body text (defaults
5225                                    to the XHTML-compliant non-breaking space character '&#160;')
5226 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5227 progress          Boolean          True to display a progress bar (defaults to false)
5228 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5229 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5230 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5231 title             String           The title text
5232 value             String           The string value to set into the active textbox element if displayed
5233 wait              Boolean          True to display a progress bar (defaults to false)
5234 width             Number           The width of the dialog in pixels
5235 </pre>
5236          *
5237          * Example usage:
5238          * <pre><code>
5239 Roo.Msg.show({
5240    title: 'Address',
5241    msg: 'Please enter your address:',
5242    width: 300,
5243    buttons: Roo.MessageBox.OKCANCEL,
5244    multiline: true,
5245    fn: saveAddress,
5246    animEl: 'addAddressBtn'
5247 });
5248 </code></pre>
5249          * @param {Object} config Configuration options
5250          * @return {Roo.MessageBox} This message box
5251          */
5252         show : function(options)
5253         {
5254             
5255             // this causes nightmares if you show one dialog after another
5256             // especially on callbacks..
5257              
5258             if(this.isVisible()){
5259                 
5260                 this.hide();
5261                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5262                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5263                 Roo.log("New Dialog Message:" +  options.msg )
5264                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5265                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5266                 
5267             }
5268             var d = this.getDialog();
5269             opt = options;
5270             d.setTitle(opt.title || "&#160;");
5271             d.closeEl.setDisplayed(opt.closable !== false);
5272             activeTextEl = textboxEl;
5273             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5274             if(opt.prompt){
5275                 if(opt.multiline){
5276                     textboxEl.hide();
5277                     textareaEl.show();
5278                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5279                         opt.multiline : this.defaultTextHeight);
5280                     activeTextEl = textareaEl;
5281                 }else{
5282                     textboxEl.show();
5283                     textareaEl.hide();
5284                 }
5285             }else{
5286                 textboxEl.hide();
5287                 textareaEl.hide();
5288             }
5289             progressEl.setDisplayed(opt.progress === true);
5290             if (opt.progress) {
5291                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5292             }
5293             this.updateProgress(0);
5294             activeTextEl.dom.value = opt.value || "";
5295             if(opt.prompt){
5296                 dlg.setDefaultButton(activeTextEl);
5297             }else{
5298                 var bs = opt.buttons;
5299                 var db = null;
5300                 if(bs && bs.ok){
5301                     db = buttons["ok"];
5302                 }else if(bs && bs.yes){
5303                     db = buttons["yes"];
5304                 }
5305                 dlg.setDefaultButton(db);
5306             }
5307             bwidth = updateButtons(opt.buttons);
5308             this.updateText(opt.msg);
5309             if(opt.cls){
5310                 d.el.addClass(opt.cls);
5311             }
5312             d.proxyDrag = opt.proxyDrag === true;
5313             d.modal = opt.modal !== false;
5314             d.mask = opt.modal !== false ? mask : false;
5315             if(!d.isVisible()){
5316                 // force it to the end of the z-index stack so it gets a cursor in FF
5317                 document.body.appendChild(dlg.el.dom);
5318                 d.animateTarget = null;
5319                 d.show(options.animEl);
5320             }
5321             return this;
5322         },
5323
5324         /**
5325          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5326          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5327          * and closing the message box when the process is complete.
5328          * @param {String} title The title bar text
5329          * @param {String} msg The message box body text
5330          * @return {Roo.MessageBox} This message box
5331          */
5332         progress : function(title, msg){
5333             this.show({
5334                 title : title,
5335                 msg : msg,
5336                 buttons: false,
5337                 progress:true,
5338                 closable:false,
5339                 minWidth: this.minProgressWidth,
5340                 modal : true
5341             });
5342             return this;
5343         },
5344
5345         /**
5346          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5347          * If a callback function is passed it will be called after the user clicks the button, and the
5348          * id of the button that was clicked will be passed as the only parameter to the callback
5349          * (could also be the top-right close button).
5350          * @param {String} title The title bar text
5351          * @param {String} msg The message box body text
5352          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5353          * @param {Object} scope (optional) The scope of the callback function
5354          * @return {Roo.MessageBox} This message box
5355          */
5356         alert : function(title, msg, fn, scope)
5357         {
5358             this.show({
5359                 title : title,
5360                 msg : msg,
5361                 buttons: this.OK,
5362                 fn: fn,
5363                 closable : false,
5364                 scope : scope,
5365                 modal : true
5366             });
5367             return this;
5368         },
5369
5370         /**
5371          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5372          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5373          * You are responsible for closing the message box when the process is complete.
5374          * @param {String} msg The message box body text
5375          * @param {String} title (optional) The title bar text
5376          * @return {Roo.MessageBox} This message box
5377          */
5378         wait : function(msg, title){
5379             this.show({
5380                 title : title,
5381                 msg : msg,
5382                 buttons: false,
5383                 closable:false,
5384                 progress:true,
5385                 modal:true,
5386                 width:300,
5387                 wait:true
5388             });
5389             waitTimer = Roo.TaskMgr.start({
5390                 run: function(i){
5391                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5392                 },
5393                 interval: 1000
5394             });
5395             return this;
5396         },
5397
5398         /**
5399          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5400          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5401          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5402          * @param {String} title The title bar text
5403          * @param {String} msg The message box body text
5404          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5405          * @param {Object} scope (optional) The scope of the callback function
5406          * @return {Roo.MessageBox} This message box
5407          */
5408         confirm : function(title, msg, fn, scope){
5409             this.show({
5410                 title : title,
5411                 msg : msg,
5412                 buttons: this.YESNO,
5413                 fn: fn,
5414                 scope : scope,
5415                 modal : true
5416             });
5417             return this;
5418         },
5419
5420         /**
5421          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5422          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5423          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5424          * (could also be the top-right close button) and the text that was entered will be passed as the two
5425          * parameters to the callback.
5426          * @param {String} title The title bar text
5427          * @param {String} msg The message box body text
5428          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5429          * @param {Object} scope (optional) The scope of the callback function
5430          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5431          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5432          * @return {Roo.MessageBox} This message box
5433          */
5434         prompt : function(title, msg, fn, scope, multiline){
5435             this.show({
5436                 title : title,
5437                 msg : msg,
5438                 buttons: this.OKCANCEL,
5439                 fn: fn,
5440                 minWidth:250,
5441                 scope : scope,
5442                 prompt:true,
5443                 multiline: multiline,
5444                 modal : true
5445             });
5446             return this;
5447         },
5448
5449         /**
5450          * Button config that displays a single OK button
5451          * @type Object
5452          */
5453         OK : {ok:true},
5454         /**
5455          * Button config that displays Yes and No buttons
5456          * @type Object
5457          */
5458         YESNO : {yes:true, no:true},
5459         /**
5460          * Button config that displays OK and Cancel buttons
5461          * @type Object
5462          */
5463         OKCANCEL : {ok:true, cancel:true},
5464         /**
5465          * Button config that displays Yes, No and Cancel buttons
5466          * @type Object
5467          */
5468         YESNOCANCEL : {yes:true, no:true, cancel:true},
5469
5470         /**
5471          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5472          * @type Number
5473          */
5474         defaultTextHeight : 75,
5475         /**
5476          * The maximum width in pixels of the message box (defaults to 600)
5477          * @type Number
5478          */
5479         maxWidth : 600,
5480         /**
5481          * The minimum width in pixels of the message box (defaults to 100)
5482          * @type Number
5483          */
5484         minWidth : 100,
5485         /**
5486          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5487          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5488          * @type Number
5489          */
5490         minProgressWidth : 250,
5491         /**
5492          * An object containing the default button text strings that can be overriden for localized language support.
5493          * Supported properties are: ok, cancel, yes and no.
5494          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5495          * @type Object
5496          */
5497         buttonText : {
5498             ok : "OK",
5499             cancel : "Cancel",
5500             yes : "Yes",
5501             no : "No"
5502         }
5503     };
5504 }();
5505
5506 /**
5507  * Shorthand for {@link Roo.MessageBox}
5508  */
5509 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5510 Roo.Msg = Roo.Msg || Roo.MessageBox;
5511 /*
5512  * - LGPL
5513  *
5514  * navbar
5515  * 
5516  */
5517
5518 /**
5519  * @class Roo.bootstrap.Navbar
5520  * @extends Roo.bootstrap.Component
5521  * Bootstrap Navbar class
5522
5523  * @constructor
5524  * Create a new Navbar
5525  * @param {Object} config The config object
5526  */
5527
5528
5529 Roo.bootstrap.Navbar = function(config){
5530     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531     this.addEvents({
5532         // raw events
5533         /**
5534          * @event beforetoggle
5535          * Fire before toggle the menu
5536          * @param {Roo.EventObject} e
5537          */
5538         "beforetoggle" : true
5539     });
5540 };
5541
5542 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5543     
5544     
5545    
5546     // private
5547     navItems : false,
5548     loadMask : false,
5549     
5550     
5551     getAutoCreate : function(){
5552         
5553         
5554         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555         
5556     },
5557     
5558     initEvents :function ()
5559     {
5560         //Roo.log(this.el.select('.navbar-toggle',true));
5561         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5562         
5563         var mark = {
5564             tag: "div",
5565             cls:"x-dlg-mask"
5566         };
5567         
5568         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5569         
5570         var size = this.el.getSize();
5571         this.maskEl.setSize(size.width, size.height);
5572         this.maskEl.enableDisplayMode("block");
5573         this.maskEl.hide();
5574         
5575         if(this.loadMask){
5576             this.maskEl.show();
5577         }
5578     },
5579     
5580     
5581     getChildContainer : function()
5582     {
5583         if (this.el && this.el.select('.collapse').getCount()) {
5584             return this.el.select('.collapse',true).first();
5585         }
5586         
5587         return this.el;
5588     },
5589     
5590     mask : function()
5591     {
5592         this.maskEl.show();
5593     },
5594     
5595     unmask : function()
5596     {
5597         this.maskEl.hide();
5598     },
5599     onToggle : function()
5600     {
5601         
5602         if(this.fireEvent('beforetoggle', this) === false){
5603             return;
5604         }
5605         var ce = this.el.select('.navbar-collapse',true).first();
5606       
5607         if (!ce.hasClass('show')) {
5608            this.expand();
5609         } else {
5610             this.collapse();
5611         }
5612         
5613         
5614     
5615     },
5616     /**
5617      * Expand the navbar pulldown 
5618      */
5619     expand : function ()
5620     {
5621        
5622         var ce = this.el.select('.navbar-collapse',true).first();
5623         if (ce.hasClass('collapsing')) {
5624             return;
5625         }
5626         ce.dom.style.height = '';
5627                // show it...
5628         ce.addClass('in'); // old...
5629         ce.removeClass('collapse');
5630         ce.addClass('show');
5631         var h = ce.getHeight();
5632         Roo.log(h);
5633         ce.removeClass('show');
5634         // at this point we should be able to see it..
5635         ce.addClass('collapsing');
5636         
5637         ce.setHeight(0); // resize it ...
5638         ce.on('transitionend', function() {
5639             //Roo.log('done transition');
5640             ce.removeClass('collapsing');
5641             ce.addClass('show');
5642             ce.removeClass('collapse');
5643
5644             ce.dom.style.height = '';
5645         }, this, { single: true} );
5646         ce.setHeight(h);
5647         ce.dom.scrollTop = 0;
5648     },
5649     /**
5650      * Collapse the navbar pulldown 
5651      */
5652     collapse : function()
5653     {
5654          var ce = this.el.select('.navbar-collapse',true).first();
5655        
5656         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5657             // it's collapsed or collapsing..
5658             return;
5659         }
5660         ce.removeClass('in'); // old...
5661         ce.setHeight(ce.getHeight());
5662         ce.removeClass('show');
5663         ce.addClass('collapsing');
5664         
5665         ce.on('transitionend', function() {
5666             ce.dom.style.height = '';
5667             ce.removeClass('collapsing');
5668             ce.addClass('collapse');
5669         }, this, { single: true} );
5670         ce.setHeight(0);
5671     }
5672     
5673     
5674     
5675 });
5676
5677
5678
5679  
5680
5681  /*
5682  * - LGPL
5683  *
5684  * navbar
5685  * 
5686  */
5687
5688 /**
5689  * @class Roo.bootstrap.NavSimplebar
5690  * @extends Roo.bootstrap.Navbar
5691  * Bootstrap Sidebar class
5692  *
5693  * @cfg {Boolean} inverse is inverted color
5694  * 
5695  * @cfg {String} type (nav | pills | tabs)
5696  * @cfg {Boolean} arrangement stacked | justified
5697  * @cfg {String} align (left | right) alignment
5698  * 
5699  * @cfg {Boolean} main (true|false) main nav bar? default false
5700  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5701  * 
5702  * @cfg {String} tag (header|footer|nav|div) default is nav 
5703
5704  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705  * 
5706  * 
5707  * @constructor
5708  * Create a new Sidebar
5709  * @param {Object} config The config object
5710  */
5711
5712
5713 Roo.bootstrap.NavSimplebar = function(config){
5714     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5715 };
5716
5717 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5718     
5719     inverse: false,
5720     
5721     type: false,
5722     arrangement: '',
5723     align : false,
5724     
5725     weight : 'light',
5726     
5727     main : false,
5728     
5729     
5730     tag : false,
5731     
5732     
5733     getAutoCreate : function(){
5734         
5735         
5736         var cfg = {
5737             tag : this.tag || 'div',
5738             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5739         };
5740         if (['light','white'].indexOf(this.weight) > -1) {
5741             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5742         }
5743         cfg.cls += ' bg-' + this.weight;
5744         
5745         if (this.inverse) {
5746             cfg.cls += ' navbar-inverse';
5747             
5748         }
5749         
5750         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5751         
5752         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5753             return cfg;
5754         }
5755         
5756         
5757     
5758         
5759         cfg.cn = [
5760             {
5761                 cls: 'nav nav-' + this.xtype,
5762                 tag : 'ul'
5763             }
5764         ];
5765         
5766          
5767         this.type = this.type || 'nav';
5768         if (['tabs','pills'].indexOf(this.type) != -1) {
5769             cfg.cn[0].cls += ' nav-' + this.type
5770         
5771         
5772         } else {
5773             if (this.type!=='nav') {
5774                 Roo.log('nav type must be nav/tabs/pills')
5775             }
5776             cfg.cn[0].cls += ' navbar-nav'
5777         }
5778         
5779         
5780         
5781         
5782         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5783             cfg.cn[0].cls += ' nav-' + this.arrangement;
5784         }
5785         
5786         
5787         if (this.align === 'right') {
5788             cfg.cn[0].cls += ' navbar-right';
5789         }
5790         
5791         
5792         
5793         
5794         return cfg;
5795     
5796         
5797     }
5798     
5799     
5800     
5801 });
5802
5803
5804
5805  
5806
5807  
5808        /*
5809  * - LGPL
5810  *
5811  * navbar
5812  * navbar-fixed-top
5813  * navbar-expand-md  fixed-top 
5814  */
5815
5816 /**
5817  * @class Roo.bootstrap.NavHeaderbar
5818  * @extends Roo.bootstrap.NavSimplebar
5819  * Bootstrap Sidebar class
5820  *
5821  * @cfg {String} brand what is brand
5822  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5823  * @cfg {String} brand_href href of the brand
5824  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5825  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5826  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5827  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5828  * 
5829  * @constructor
5830  * Create a new Sidebar
5831  * @param {Object} config The config object
5832  */
5833
5834
5835 Roo.bootstrap.NavHeaderbar = function(config){
5836     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837       
5838 };
5839
5840 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5841     
5842     position: '',
5843     brand: '',
5844     brand_href: false,
5845     srButton : true,
5846     autohide : false,
5847     desktopCenter : false,
5848    
5849     
5850     getAutoCreate : function(){
5851         
5852         var   cfg = {
5853             tag: this.nav || 'nav',
5854             cls: 'navbar navbar-expand-md',
5855             role: 'navigation',
5856             cn: []
5857         };
5858         
5859         var cn = cfg.cn;
5860         if (this.desktopCenter) {
5861             cn.push({cls : 'container', cn : []});
5862             cn = cn[0].cn;
5863         }
5864         
5865         if(this.srButton){
5866             var btn = {
5867                 tag: 'button',
5868                 type: 'button',
5869                 cls: 'navbar-toggle navbar-toggler',
5870                 'data-toggle': 'collapse',
5871                 cn: [
5872                     {
5873                         tag: 'span',
5874                         cls: 'sr-only',
5875                         html: 'Toggle navigation'
5876                     },
5877                     {
5878                         tag: 'span',
5879                         cls: 'icon-bar navbar-toggler-icon'
5880                     },
5881                     {
5882                         tag: 'span',
5883                         cls: 'icon-bar'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar'
5888                     }
5889                 ]
5890             };
5891             
5892             cn.push( Roo.bootstrap.version == 4 ? btn : {
5893                 tag: 'div',
5894                 cls: 'navbar-header',
5895                 cn: [
5896                     btn
5897                 ]
5898             });
5899         }
5900         
5901         cn.push({
5902             tag: 'div',
5903             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904             cn : []
5905         });
5906         
5907         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5908         
5909         if (['light','white'].indexOf(this.weight) > -1) {
5910             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5911         }
5912         cfg.cls += ' bg-' + this.weight;
5913         
5914         
5915         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5916             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5917             
5918             // tag can override this..
5919             
5920             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5921         }
5922         
5923         if (this.brand !== '') {
5924             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5925             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5926                 tag: 'a',
5927                 href: this.brand_href ? this.brand_href : '#',
5928                 cls: 'navbar-brand',
5929                 cn: [
5930                 this.brand
5931                 ]
5932             });
5933         }
5934         
5935         if(this.main){
5936             cfg.cls += ' main-nav';
5937         }
5938         
5939         
5940         return cfg;
5941
5942         
5943     },
5944     getHeaderChildContainer : function()
5945     {
5946         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5947             return this.el.select('.navbar-header',true).first();
5948         }
5949         
5950         return this.getChildContainer();
5951     },
5952     
5953     getChildContainer : function()
5954     {
5955          
5956         return this.el.select('.roo-navbar-collapse',true).first();
5957          
5958         
5959     },
5960     
5961     initEvents : function()
5962     {
5963         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5964         
5965         if (this.autohide) {
5966             
5967             var prevScroll = 0;
5968             var ft = this.el;
5969             
5970             Roo.get(document).on('scroll',function(e) {
5971                 var ns = Roo.get(document).getScroll().top;
5972                 var os = prevScroll;
5973                 prevScroll = ns;
5974                 
5975                 if(ns > os){
5976                     ft.removeClass('slideDown');
5977                     ft.addClass('slideUp');
5978                     return;
5979                 }
5980                 ft.removeClass('slideUp');
5981                 ft.addClass('slideDown');
5982                  
5983               
5984           },this);
5985         }
5986     }    
5987     
5988 });
5989
5990
5991
5992  
5993
5994  /*
5995  * - LGPL
5996  *
5997  * navbar
5998  * 
5999  */
6000
6001 /**
6002  * @class Roo.bootstrap.NavSidebar
6003  * @extends Roo.bootstrap.Navbar
6004  * Bootstrap Sidebar class
6005  * 
6006  * @constructor
6007  * Create a new Sidebar
6008  * @param {Object} config The config object
6009  */
6010
6011
6012 Roo.bootstrap.NavSidebar = function(config){
6013     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6014 };
6015
6016 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6017     
6018     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6019     
6020     getAutoCreate : function(){
6021         
6022         
6023         return  {
6024             tag: 'div',
6025             cls: 'sidebar sidebar-nav'
6026         };
6027     
6028         
6029     }
6030     
6031     
6032     
6033 });
6034
6035
6036
6037  
6038
6039  /*
6040  * - LGPL
6041  *
6042  * nav group
6043  * 
6044  */
6045
6046 /**
6047  * @class Roo.bootstrap.NavGroup
6048  * @extends Roo.bootstrap.Component
6049  * Bootstrap NavGroup class
6050  * @cfg {String} align (left|right)
6051  * @cfg {Boolean} inverse
6052  * @cfg {String} type (nav|pills|tab) default nav
6053  * @cfg {String} navId - reference Id for navbar.
6054  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6055  * 
6056  * @constructor
6057  * Create a new nav group
6058  * @param {Object} config The config object
6059  */
6060
6061 Roo.bootstrap.NavGroup = function(config){
6062     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6063     this.navItems = [];
6064    
6065     Roo.bootstrap.NavGroup.register(this);
6066      this.addEvents({
6067         /**
6068              * @event changed
6069              * Fires when the active item changes
6070              * @param {Roo.bootstrap.NavGroup} this
6071              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6072              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6073          */
6074         'changed': true
6075      });
6076     
6077 };
6078
6079 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6080     
6081     align: '',
6082     inverse: false,
6083     form: false,
6084     type: 'nav',
6085     navId : '',
6086     // private
6087     pilltype : true,
6088     
6089     navItems : false, 
6090     
6091     getAutoCreate : function()
6092     {
6093         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6094         
6095         cfg = {
6096             tag : 'ul',
6097             cls: 'nav' 
6098         };
6099         if (Roo.bootstrap.version == 4) {
6100             if (['tabs','pills'].indexOf(this.type) != -1) {
6101                 cfg.cls += ' nav-' + this.type; 
6102             } else {
6103                 // trying to remove so header bar can right align top?
6104                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6105                     // do not use on header bar... 
6106                     cfg.cls += ' navbar-nav';
6107                 }
6108             }
6109             
6110         } else {
6111             if (['tabs','pills'].indexOf(this.type) != -1) {
6112                 cfg.cls += ' nav-' + this.type
6113             } else {
6114                 if (this.type !== 'nav') {
6115                     Roo.log('nav type must be nav/tabs/pills')
6116                 }
6117                 cfg.cls += ' navbar-nav'
6118             }
6119         }
6120         
6121         if (this.parent() && this.parent().sidebar) {
6122             cfg = {
6123                 tag: 'ul',
6124                 cls: 'dashboard-menu sidebar-menu'
6125             };
6126             
6127             return cfg;
6128         }
6129         
6130         if (this.form === true) {
6131             cfg = {
6132                 tag: 'form',
6133                 cls: 'navbar-form form-inline'
6134             };
6135             //nav navbar-right ml-md-auto
6136             if (this.align === 'right') {
6137                 cfg.cls += ' navbar-right ml-md-auto';
6138             } else {
6139                 cfg.cls += ' navbar-left';
6140             }
6141         }
6142         
6143         if (this.align === 'right') {
6144             cfg.cls += ' navbar-right ml-md-auto';
6145         } else {
6146             cfg.cls += ' mr-auto';
6147         }
6148         
6149         if (this.inverse) {
6150             cfg.cls += ' navbar-inverse';
6151             
6152         }
6153         
6154         
6155         return cfg;
6156     },
6157     /**
6158     * sets the active Navigation item
6159     * @param {Roo.bootstrap.NavItem} the new current navitem
6160     */
6161     setActiveItem : function(item)
6162     {
6163         var prev = false;
6164         Roo.each(this.navItems, function(v){
6165             if (v == item) {
6166                 return ;
6167             }
6168             if (v.isActive()) {
6169                 v.setActive(false, true);
6170                 prev = v;
6171                 
6172             }
6173             
6174         });
6175
6176         item.setActive(true, true);
6177         this.fireEvent('changed', this, item, prev);
6178         
6179         
6180     },
6181     /**
6182     * gets the active Navigation item
6183     * @return {Roo.bootstrap.NavItem} the current navitem
6184     */
6185     getActive : function()
6186     {
6187         
6188         var prev = false;
6189         Roo.each(this.navItems, function(v){
6190             
6191             if (v.isActive()) {
6192                 prev = v;
6193                 
6194             }
6195             
6196         });
6197         return prev;
6198     },
6199     
6200     indexOfNav : function()
6201     {
6202         
6203         var prev = false;
6204         Roo.each(this.navItems, function(v,i){
6205             
6206             if (v.isActive()) {
6207                 prev = i;
6208                 
6209             }
6210             
6211         });
6212         return prev;
6213     },
6214     /**
6215     * adds a Navigation item
6216     * @param {Roo.bootstrap.NavItem} the navitem to add
6217     */
6218     addItem : function(cfg)
6219     {
6220         if (this.form && Roo.bootstrap.version == 4) {
6221             cfg.tag = 'div';
6222         }
6223         var cn = new Roo.bootstrap.NavItem(cfg);
6224         this.register(cn);
6225         cn.parentId = this.id;
6226         cn.onRender(this.el, null);
6227         return cn;
6228     },
6229     /**
6230     * register a Navigation item
6231     * @param {Roo.bootstrap.NavItem} the navitem to add
6232     */
6233     register : function(item)
6234     {
6235         this.navItems.push( item);
6236         item.navId = this.navId;
6237     
6238     },
6239     
6240     /**
6241     * clear all the Navigation item
6242     */
6243    
6244     clearAll : function()
6245     {
6246         this.navItems = [];
6247         this.el.dom.innerHTML = '';
6248     },
6249     
6250     getNavItem: function(tabId)
6251     {
6252         var ret = false;
6253         Roo.each(this.navItems, function(e) {
6254             if (e.tabId == tabId) {
6255                ret =  e;
6256                return false;
6257             }
6258             return true;
6259             
6260         });
6261         return ret;
6262     },
6263     
6264     setActiveNext : function()
6265     {
6266         var i = this.indexOfNav(this.getActive());
6267         if (i > this.navItems.length) {
6268             return;
6269         }
6270         this.setActiveItem(this.navItems[i+1]);
6271     },
6272     setActivePrev : function()
6273     {
6274         var i = this.indexOfNav(this.getActive());
6275         if (i  < 1) {
6276             return;
6277         }
6278         this.setActiveItem(this.navItems[i-1]);
6279     },
6280     clearWasActive : function(except) {
6281         Roo.each(this.navItems, function(e) {
6282             if (e.tabId != except.tabId && e.was_active) {
6283                e.was_active = false;
6284                return false;
6285             }
6286             return true;
6287             
6288         });
6289     },
6290     getWasActive : function ()
6291     {
6292         var r = false;
6293         Roo.each(this.navItems, function(e) {
6294             if (e.was_active) {
6295                r = e;
6296                return false;
6297             }
6298             return true;
6299             
6300         });
6301         return r;
6302     }
6303     
6304     
6305 });
6306
6307  
6308 Roo.apply(Roo.bootstrap.NavGroup, {
6309     
6310     groups: {},
6311      /**
6312     * register a Navigation Group
6313     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6314     */
6315     register : function(navgrp)
6316     {
6317         this.groups[navgrp.navId] = navgrp;
6318         
6319     },
6320     /**
6321     * fetch a Navigation Group based on the navigation ID
6322     * @param {string} the navgroup to add
6323     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6324     */
6325     get: function(navId) {
6326         if (typeof(this.groups[navId]) == 'undefined') {
6327             return false;
6328             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6329         }
6330         return this.groups[navId] ;
6331     }
6332     
6333     
6334     
6335 });
6336
6337  /*
6338  * - LGPL
6339  *
6340  * row
6341  * 
6342  */
6343
6344 /**
6345  * @class Roo.bootstrap.NavItem
6346  * @extends Roo.bootstrap.Component
6347  * Bootstrap Navbar.NavItem class
6348  * @cfg {String} href  link to
6349  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6350  * @cfg {Boolean} button_outline show and outlined button
6351  * @cfg {String} html content of button
6352  * @cfg {String} badge text inside badge
6353  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6354  * @cfg {String} glyphicon DEPRICATED - use fa
6355  * @cfg {String} icon DEPRICATED - use fa
6356  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6357  * @cfg {Boolean} active Is item active
6358  * @cfg {Boolean} disabled Is item disabled
6359  * @cfg {String} linkcls  Link Class
6360  * @cfg {Boolean} preventDefault (true | false) default false
6361  * @cfg {String} tabId the tab that this item activates.
6362  * @cfg {String} tagtype (a|span) render as a href or span?
6363  * @cfg {Boolean} animateRef (true|false) link to element default false  
6364   
6365  * @constructor
6366  * Create a new Navbar Item
6367  * @param {Object} config The config object
6368  */
6369 Roo.bootstrap.NavItem = function(config){
6370     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6371     this.addEvents({
6372         // raw events
6373         /**
6374          * @event click
6375          * The raw click event for the entire grid.
6376          * @param {Roo.EventObject} e
6377          */
6378         "click" : true,
6379          /**
6380             * @event changed
6381             * Fires when the active item active state changes
6382             * @param {Roo.bootstrap.NavItem} this
6383             * @param {boolean} state the new state
6384              
6385          */
6386         'changed': true,
6387         /**
6388             * @event scrollto
6389             * Fires when scroll to element
6390             * @param {Roo.bootstrap.NavItem} this
6391             * @param {Object} options
6392             * @param {Roo.EventObject} e
6393              
6394          */
6395         'scrollto': true
6396     });
6397    
6398 };
6399
6400 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6401     
6402     href: false,
6403     html: '',
6404     badge: '',
6405     icon: false,
6406     fa : false,
6407     glyphicon: false,
6408     active: false,
6409     preventDefault : false,
6410     tabId : false,
6411     tagtype : 'a',
6412     tag: 'li',
6413     disabled : false,
6414     animateRef : false,
6415     was_active : false,
6416     button_weight : '',
6417     button_outline : false,
6418     linkcls : '',
6419     navLink: false,
6420     
6421     getAutoCreate : function(){
6422          
6423         var cfg = {
6424             tag: this.tag,
6425             cls: 'nav-item'
6426         };
6427         
6428         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6429         
6430         if (this.active) {
6431             cfg.cls +=  ' active' ;
6432         }
6433         if (this.disabled) {
6434             cfg.cls += ' disabled';
6435         }
6436         
6437         // BS4 only?
6438         if (this.button_weight.length) {
6439             cfg.tag = this.href ? 'a' : 'button';
6440             cfg.html = this.html || '';
6441             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6442             if (this.href) {
6443                 cfg.href = this.href;
6444             }
6445             if (this.fa) {
6446                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6447             } else {
6448                 cfg.cls += " nav-html";
6449             }
6450             
6451             // menu .. should add dropdown-menu class - so no need for carat..
6452             
6453             if (this.badge !== '') {
6454                  
6455                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6456             }
6457             return cfg;
6458         }
6459         
6460         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461             cfg.cn = [
6462                 {
6463                     tag: this.tagtype,
6464                     href : this.href || "#",
6465                     html: this.html || '',
6466                     cls : ''
6467                 }
6468             ];
6469             if (this.tagtype == 'a') {
6470                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6471         
6472             }
6473             if (this.icon) {
6474                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else  if (this.fa) {
6476                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6477             } else if(this.glyphicon) {
6478                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6479             } else {
6480                 cfg.cn[0].cls += " nav-html";
6481             }
6482             
6483             if (this.menu) {
6484                 cfg.cn[0].html += " <span class='caret'></span>";
6485              
6486             }
6487             
6488             if (this.badge !== '') {
6489                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6490             }
6491         }
6492         
6493         
6494         
6495         return cfg;
6496     },
6497     onRender : function(ct, position)
6498     {
6499        // Roo.log("Call onRender: " + this.xtype);
6500         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501             this.tag = 'div';
6502         }
6503         
6504         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6505         this.navLink = this.el.select('.nav-link',true).first();
6506         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6507         return ret;
6508     },
6509       
6510     
6511     initEvents: function() 
6512     {
6513         if (typeof (this.menu) != 'undefined') {
6514             this.menu.parentType = this.xtype;
6515             this.menu.triggerEl = this.el;
6516             this.menu = this.addxtype(Roo.apply({}, this.menu));
6517         }
6518         
6519         this.el.on('click', this.onClick, this);
6520         
6521         //if(this.tagtype == 'span'){
6522         //    this.el.select('span',true).on('click', this.onClick, this);
6523         //}
6524        
6525         // at this point parent should be available..
6526         this.parent().register(this);
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if (e.getTarget('.dropdown-menu-item')) {
6532             // did you click on a menu itemm.... - then don't trigger onclick..
6533             return;
6534         }
6535         
6536         if(
6537                 this.preventDefault || 
6538                 this.href == '#' 
6539         ){
6540             Roo.log("NavItem - prevent Default?");
6541             e.preventDefault();
6542         }
6543         
6544         if (this.disabled) {
6545             return;
6546         }
6547         
6548         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6549         if (tg && tg.transition) {
6550             Roo.log("waiting for the transitionend");
6551             return;
6552         }
6553         
6554         
6555         
6556         //Roo.log("fire event clicked");
6557         if(this.fireEvent('click', this, e) === false){
6558             return;
6559         };
6560         
6561         if(this.tagtype == 'span'){
6562             return;
6563         }
6564         
6565         //Roo.log(this.href);
6566         var ael = this.el.select('a',true).first();
6567         //Roo.log(ael);
6568         
6569         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6570             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6571             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6572                 return; // ignore... - it's a 'hash' to another page.
6573             }
6574             Roo.log("NavItem - prevent Default?");
6575             e.preventDefault();
6576             this.scrollToElement(e);
6577         }
6578         
6579         
6580         var p =  this.parent();
6581    
6582         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6583             if (typeof(p.setActiveItem) !== 'undefined') {
6584                 p.setActiveItem(this);
6585             }
6586         }
6587         
6588         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6589         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6590             // remove the collapsed menu expand...
6591             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6592         }
6593     },
6594     
6595     isActive: function () {
6596         return this.active
6597     },
6598     setActive : function(state, fire, is_was_active)
6599     {
6600         if (this.active && !state && this.navId) {
6601             this.was_active = true;
6602             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6603             if (nv) {
6604                 nv.clearWasActive(this);
6605             }
6606             
6607         }
6608         this.active = state;
6609         
6610         if (!state ) {
6611             this.el.removeClass('active');
6612             this.navLink ? this.navLink.removeClass('active') : false;
6613         } else if (!this.el.hasClass('active')) {
6614             
6615             this.el.addClass('active');
6616             if (Roo.bootstrap.version == 4 && this.navLink ) {
6617                 this.navLink.addClass('active');
6618             }
6619             
6620         }
6621         if (fire) {
6622             this.fireEvent('changed', this, state);
6623         }
6624         
6625         // show a panel if it's registered and related..
6626         
6627         if (!this.navId || !this.tabId || !state || is_was_active) {
6628             return;
6629         }
6630         
6631         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632         if (!tg) {
6633             return;
6634         }
6635         var pan = tg.getPanelByName(this.tabId);
6636         if (!pan) {
6637             return;
6638         }
6639         // if we can not flip to new panel - go back to old nav highlight..
6640         if (false == tg.showPanel(pan)) {
6641             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6642             if (nv) {
6643                 var onav = nv.getWasActive();
6644                 if (onav) {
6645                     onav.setActive(true, false, true);
6646                 }
6647             }
6648             
6649         }
6650         
6651         
6652         
6653     },
6654      // this should not be here...
6655     setDisabled : function(state)
6656     {
6657         this.disabled = state;
6658         if (!state ) {
6659             this.el.removeClass('disabled');
6660         } else if (!this.el.hasClass('disabled')) {
6661             this.el.addClass('disabled');
6662         }
6663         
6664     },
6665     
6666     /**
6667      * Fetch the element to display the tooltip on.
6668      * @return {Roo.Element} defaults to this.el
6669      */
6670     tooltipEl : function()
6671     {
6672         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6673     },
6674     
6675     scrollToElement : function(e)
6676     {
6677         var c = document.body;
6678         
6679         /*
6680          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6681          */
6682         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6683             c = document.documentElement;
6684         }
6685         
6686         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6687         
6688         if(!target){
6689             return;
6690         }
6691
6692         var o = target.calcOffsetsTo(c);
6693         
6694         var options = {
6695             target : target,
6696             value : o[1]
6697         };
6698         
6699         this.fireEvent('scrollto', this, options, e);
6700         
6701         Roo.get(c).scrollTo('top', options.value, true);
6702         
6703         return;
6704     },
6705     /**
6706      * Set the HTML (text content) of the item
6707      * @param {string} html  content for the nav item
6708      */
6709     setHtml : function(html)
6710     {
6711         this.html = html;
6712         this.htmlEl.dom.innerHTML = html;
6713         
6714     } 
6715 });
6716  
6717
6718  /*
6719  * - LGPL
6720  *
6721  * sidebar item
6722  *
6723  *  li
6724  *    <span> icon </span>
6725  *    <span> text </span>
6726  *    <span>badge </span>
6727  */
6728
6729 /**
6730  * @class Roo.bootstrap.NavSidebarItem
6731  * @extends Roo.bootstrap.NavItem
6732  * Bootstrap Navbar.NavSidebarItem class
6733  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6734  * {Boolean} open is the menu open
6735  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6736  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6737  * {String} buttonSize (sm|md|lg)the extra classes for the button
6738  * {Boolean} showArrow show arrow next to the text (default true)
6739  * @constructor
6740  * Create a new Navbar Button
6741  * @param {Object} config The config object
6742  */
6743 Roo.bootstrap.NavSidebarItem = function(config){
6744     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6745     this.addEvents({
6746         // raw events
6747         /**
6748          * @event click
6749          * The raw click event for the entire grid.
6750          * @param {Roo.EventObject} e
6751          */
6752         "click" : true,
6753          /**
6754             * @event changed
6755             * Fires when the active item active state changes
6756             * @param {Roo.bootstrap.NavSidebarItem} this
6757             * @param {boolean} state the new state
6758              
6759          */
6760         'changed': true
6761     });
6762    
6763 };
6764
6765 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6766     
6767     badgeWeight : 'default',
6768     
6769     open: false,
6770     
6771     buttonView : false,
6772     
6773     buttonWeight : 'default',
6774     
6775     buttonSize : 'md',
6776     
6777     showArrow : true,
6778     
6779     getAutoCreate : function(){
6780         
6781         
6782         var a = {
6783                 tag: 'a',
6784                 href : this.href || '#',
6785                 cls: '',
6786                 html : '',
6787                 cn : []
6788         };
6789         
6790         if(this.buttonView){
6791             a = {
6792                 tag: 'button',
6793                 href : this.href || '#',
6794                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6795                 html : this.html,
6796                 cn : []
6797             };
6798         }
6799         
6800         var cfg = {
6801             tag: 'li',
6802             cls: '',
6803             cn: [ a ]
6804         };
6805         
6806         if (this.active) {
6807             cfg.cls += ' active';
6808         }
6809         
6810         if (this.disabled) {
6811             cfg.cls += ' disabled';
6812         }
6813         if (this.open) {
6814             cfg.cls += ' open x-open';
6815         }
6816         // left icon..
6817         if (this.glyphicon || this.icon) {
6818             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6819             a.cn.push({ tag : 'i', cls : c }) ;
6820         }
6821         
6822         if(!this.buttonView){
6823             var span = {
6824                 tag: 'span',
6825                 html : this.html || ''
6826             };
6827
6828             a.cn.push(span);
6829             
6830         }
6831         
6832         if (this.badge !== '') {
6833             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6834         }
6835         
6836         if (this.menu) {
6837             
6838             if(this.showArrow){
6839                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6840             }
6841             
6842             a.cls += ' dropdown-toggle treeview' ;
6843         }
6844         
6845         return cfg;
6846     },
6847     
6848     initEvents : function()
6849     { 
6850         if (typeof (this.menu) != 'undefined') {
6851             this.menu.parentType = this.xtype;
6852             this.menu.triggerEl = this.el;
6853             this.menu = this.addxtype(Roo.apply({}, this.menu));
6854         }
6855         
6856         this.el.on('click', this.onClick, this);
6857         
6858         if(this.badge !== ''){
6859             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6860         }
6861         
6862     },
6863     
6864     onClick : function(e)
6865     {
6866         if(this.disabled){
6867             e.preventDefault();
6868             return;
6869         }
6870         
6871         if(this.preventDefault){
6872             e.preventDefault();
6873         }
6874         
6875         this.fireEvent('click', this, e);
6876     },
6877     
6878     disable : function()
6879     {
6880         this.setDisabled(true);
6881     },
6882     
6883     enable : function()
6884     {
6885         this.setDisabled(false);
6886     },
6887     
6888     setDisabled : function(state)
6889     {
6890         if(this.disabled == state){
6891             return;
6892         }
6893         
6894         this.disabled = state;
6895         
6896         if (state) {
6897             this.el.addClass('disabled');
6898             return;
6899         }
6900         
6901         this.el.removeClass('disabled');
6902         
6903         return;
6904     },
6905     
6906     setActive : function(state)
6907     {
6908         if(this.active == state){
6909             return;
6910         }
6911         
6912         this.active = state;
6913         
6914         if (state) {
6915             this.el.addClass('active');
6916             return;
6917         }
6918         
6919         this.el.removeClass('active');
6920         
6921         return;
6922     },
6923     
6924     isActive: function () 
6925     {
6926         return this.active;
6927     },
6928     
6929     setBadge : function(str)
6930     {
6931         if(!this.badgeEl){
6932             return;
6933         }
6934         
6935         this.badgeEl.dom.innerHTML = str;
6936     }
6937     
6938    
6939      
6940  
6941 });
6942  
6943
6944  /*
6945  * - LGPL
6946  *
6947  *  Breadcrumb Nav
6948  * 
6949  */
6950 Roo.namespace('Roo.bootstrap.breadcrumb');
6951
6952
6953 /**
6954  * @class Roo.bootstrap.breadcrumb.Nav
6955  * @extends Roo.bootstrap.Component
6956  * Bootstrap Breadcrumb Nav Class
6957  *  
6958  * @children Roo.bootstrap.breadcrumb.Item
6959  * 
6960  * @constructor
6961  * Create a new breadcrumb.Nav
6962  * @param {Object} config The config object
6963  */
6964
6965
6966 Roo.bootstrap.breadcrumb.Nav = function(config){
6967     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6968     
6969     
6970 };
6971
6972 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6973     
6974     getAutoCreate : function()
6975     {
6976
6977         var cfg = {
6978             tag: 'nav',
6979             cn : [
6980                 {
6981                     tag : 'ol',
6982                     cls : 'breadcrumb'
6983                 }
6984             ]
6985             
6986         };
6987           
6988         return cfg;
6989     },
6990     
6991     initEvents: function()
6992     {
6993         this.olEl = this.el.select('ol',true).first();    
6994     },
6995     getChildContainer : function()
6996     {
6997         return this.olEl;  
6998     }
6999     
7000 });
7001
7002  /*
7003  * - LGPL
7004  *
7005  *  Breadcrumb Item
7006  * 
7007  */
7008
7009
7010 /**
7011  * @class Roo.bootstrap.breadcrumb.Nav
7012  * @extends Roo.bootstrap.Component
7013  * Bootstrap Breadcrumb Nav Class
7014  *  
7015  * @children Roo.bootstrap.breadcrumb.Component
7016  * @cfg {String} html the content of the link.
7017  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7018  * @cfg {Boolean} active is it active
7019
7020  * 
7021  * @constructor
7022  * Create a new breadcrumb.Nav
7023  * @param {Object} config The config object
7024  */
7025
7026 Roo.bootstrap.breadcrumb.Item = function(config){
7027     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7028     this.addEvents({
7029         // img events
7030         /**
7031          * @event click
7032          * The img click event for the img.
7033          * @param {Roo.EventObject} e
7034          */
7035         "click" : true
7036     });
7037     
7038 };
7039
7040 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7041     
7042     href: false,
7043     html : '',
7044     
7045     getAutoCreate : function()
7046     {
7047
7048         var cfg = {
7049             tag: 'li',
7050             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7051         };
7052         if (this.href !== false) {
7053             cfg.cn = [{
7054                 tag : 'a',
7055                 href : this.href,
7056                 html : this.html
7057             }];
7058         } else {
7059             cfg.html = this.html;
7060         }
7061         
7062         return cfg;
7063     },
7064     
7065     initEvents: function()
7066     {
7067         if (this.href) {
7068             this.el.select('a', true).first().on('click',this.onClick, this)
7069         }
7070         
7071     },
7072     onClick : function(e)
7073     {
7074         e.preventDefault();
7075         this.fireEvent('click',this,  e);
7076     }
7077     
7078 });
7079
7080  /*
7081  * - LGPL
7082  *
7083  * row
7084  * 
7085  */
7086
7087 /**
7088  * @class Roo.bootstrap.Row
7089  * @extends Roo.bootstrap.Component
7090  * Bootstrap Row class (contains columns...)
7091  * 
7092  * @constructor
7093  * Create a new Row
7094  * @param {Object} config The config object
7095  */
7096
7097 Roo.bootstrap.Row = function(config){
7098     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7099 };
7100
7101 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7102     
7103     getAutoCreate : function(){
7104        return {
7105             cls: 'row clearfix'
7106        };
7107     }
7108     
7109     
7110 });
7111
7112  
7113
7114  /*
7115  * - LGPL
7116  *
7117  * pagination
7118  * 
7119  */
7120
7121 /**
7122  * @class Roo.bootstrap.Pagination
7123  * @extends Roo.bootstrap.Component
7124  * Bootstrap Pagination class
7125  * @cfg {String} size xs | sm | md | lg
7126  * @cfg {Boolean} inverse false | true
7127  * 
7128  * @constructor
7129  * Create a new Pagination
7130  * @param {Object} config The config object
7131  */
7132
7133 Roo.bootstrap.Pagination = function(config){
7134     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7135 };
7136
7137 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7138     
7139     cls: false,
7140     size: false,
7141     inverse: false,
7142     
7143     getAutoCreate : function(){
7144         var cfg = {
7145             tag: 'ul',
7146                 cls: 'pagination'
7147         };
7148         if (this.inverse) {
7149             cfg.cls += ' inverse';
7150         }
7151         if (this.html) {
7152             cfg.html=this.html;
7153         }
7154         if (this.cls) {
7155             cfg.cls += " " + this.cls;
7156         }
7157         return cfg;
7158     }
7159    
7160 });
7161
7162  
7163
7164  /*
7165  * - LGPL
7166  *
7167  * Pagination item
7168  * 
7169  */
7170
7171
7172 /**
7173  * @class Roo.bootstrap.PaginationItem
7174  * @extends Roo.bootstrap.Component
7175  * Bootstrap PaginationItem class
7176  * @cfg {String} html text
7177  * @cfg {String} href the link
7178  * @cfg {Boolean} preventDefault (true | false) default true
7179  * @cfg {Boolean} active (true | false) default false
7180  * @cfg {Boolean} disabled default false
7181  * 
7182  * 
7183  * @constructor
7184  * Create a new PaginationItem
7185  * @param {Object} config The config object
7186  */
7187
7188
7189 Roo.bootstrap.PaginationItem = function(config){
7190     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7191     this.addEvents({
7192         // raw events
7193         /**
7194          * @event click
7195          * The raw click event for the entire grid.
7196          * @param {Roo.EventObject} e
7197          */
7198         "click" : true
7199     });
7200 };
7201
7202 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7203     
7204     href : false,
7205     html : false,
7206     preventDefault: true,
7207     active : false,
7208     cls : false,
7209     disabled: false,
7210     
7211     getAutoCreate : function(){
7212         var cfg= {
7213             tag: 'li',
7214             cn: [
7215                 {
7216                     tag : 'a',
7217                     href : this.href ? this.href : '#',
7218                     html : this.html ? this.html : ''
7219                 }
7220             ]
7221         };
7222         
7223         if(this.cls){
7224             cfg.cls = this.cls;
7225         }
7226         
7227         if(this.disabled){
7228             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229         }
7230         
7231         if(this.active){
7232             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7233         }
7234         
7235         return cfg;
7236     },
7237     
7238     initEvents: function() {
7239         
7240         this.el.on('click', this.onClick, this);
7241         
7242     },
7243     onClick : function(e)
7244     {
7245         Roo.log('PaginationItem on click ');
7246         if(this.preventDefault){
7247             e.preventDefault();
7248         }
7249         
7250         if(this.disabled){
7251             return;
7252         }
7253         
7254         this.fireEvent('click', this, e);
7255     }
7256    
7257 });
7258
7259  
7260
7261  /*
7262  * - LGPL
7263  *
7264  * slider
7265  * 
7266  */
7267
7268
7269 /**
7270  * @class Roo.bootstrap.Slider
7271  * @extends Roo.bootstrap.Component
7272  * Bootstrap Slider class
7273  *    
7274  * @constructor
7275  * Create a new Slider
7276  * @param {Object} config The config object
7277  */
7278
7279 Roo.bootstrap.Slider = function(config){
7280     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7281 };
7282
7283 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7284     
7285     getAutoCreate : function(){
7286         
7287         var cfg = {
7288             tag: 'div',
7289             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290             cn: [
7291                 {
7292                     tag: 'a',
7293                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7294                 }
7295             ]
7296         };
7297         
7298         return cfg;
7299     }
7300    
7301 });
7302
7303  /*
7304  * Based on:
7305  * Ext JS Library 1.1.1
7306  * Copyright(c) 2006-2007, Ext JS, LLC.
7307  *
7308  * Originally Released Under LGPL - original licence link has changed is not relivant.
7309  *
7310  * Fork - LGPL
7311  * <script type="text/javascript">
7312  */
7313  /**
7314  * @extends Roo.dd.DDProxy
7315  * @class Roo.grid.SplitDragZone
7316  * Support for Column Header resizing
7317  * @constructor
7318  * @param {Object} config
7319  */
7320 // private
7321 // This is a support class used internally by the Grid components
7322 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7323     this.grid = grid;
7324     this.view = grid.getView();
7325     this.proxy = this.view.resizeProxy;
7326     Roo.grid.SplitDragZone.superclass.constructor.call(
7327         this,
7328         hd, // ID
7329         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7330         {  // CONFIG
7331             dragElId : Roo.id(this.proxy.dom),
7332             resizeFrame:false
7333         }
7334     );
7335     
7336     this.setHandleElId(Roo.id(hd));
7337     if (hd2 !== false) {
7338         this.setOuterHandleElId(Roo.id(hd2));
7339     }
7340     
7341     this.scroll = false;
7342 };
7343 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7344     fly: Roo.Element.fly,
7345
7346     b4StartDrag : function(x, y){
7347         this.view.headersDisabled = true;
7348         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7349                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7350         );
7351         this.proxy.setHeight(h);
7352         
7353         // for old system colWidth really stored the actual width?
7354         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7355         // which in reality did not work.. - it worked only for fixed sizes
7356         // for resizable we need to use actual sizes.
7357         var w = this.cm.getColumnWidth(this.cellIndex);
7358         if (!this.view.mainWrap) {
7359             // bootstrap.
7360             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7361         }
7362         
7363         
7364         
7365         // this was w-this.grid.minColumnWidth;
7366         // doesnt really make sense? - w = thie curren width or the rendered one?
7367         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7368         this.resetConstraints();
7369         this.setXConstraint(minw, 1000);
7370         this.setYConstraint(0, 0);
7371         this.minX = x - minw;
7372         this.maxX = x + 1000;
7373         this.startPos = x;
7374         if (!this.view.mainWrap) { // this is Bootstrap code..
7375             this.getDragEl().style.display='block';
7376         }
7377         
7378         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7379     },
7380
7381
7382     handleMouseDown : function(e){
7383         ev = Roo.EventObject.setEvent(e);
7384         var t = this.fly(ev.getTarget());
7385         if(t.hasClass("x-grid-split")){
7386             this.cellIndex = this.view.getCellIndex(t.dom);
7387             this.split = t.dom;
7388             this.cm = this.grid.colModel;
7389             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7390                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7391             }
7392         }
7393     },
7394
7395     endDrag : function(e){
7396         this.view.headersDisabled = false;
7397         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7398         var diff = endX - this.startPos;
7399         // 
7400         var w = this.cm.getColumnWidth(this.cellIndex);
7401         if (!this.view.mainWrap) {
7402             w = 0;
7403         }
7404         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7405     },
7406
7407     autoOffset : function(){
7408         this.setDelta(0,0);
7409     }
7410 });/*
7411  * Based on:
7412  * Ext JS Library 1.1.1
7413  * Copyright(c) 2006-2007, Ext JS, LLC.
7414  *
7415  * Originally Released Under LGPL - original licence link has changed is not relivant.
7416  *
7417  * Fork - LGPL
7418  * <script type="text/javascript">
7419  */
7420
7421 /**
7422  * @class Roo.grid.AbstractSelectionModel
7423  * @extends Roo.util.Observable
7424  * @abstract
7425  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7426  * implemented by descendant classes.  This class should not be directly instantiated.
7427  * @constructor
7428  */
7429 Roo.grid.AbstractSelectionModel = function(){
7430     this.locked = false;
7431     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7432 };
7433
7434 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7435     /** @ignore Called by the grid automatically. Do not call directly. */
7436     init : function(grid){
7437         this.grid = grid;
7438         this.initEvents();
7439     },
7440
7441     /**
7442      * Locks the selections.
7443      */
7444     lock : function(){
7445         this.locked = true;
7446     },
7447
7448     /**
7449      * Unlocks the selections.
7450      */
7451     unlock : function(){
7452         this.locked = false;
7453     },
7454
7455     /**
7456      * Returns true if the selections are locked.
7457      * @return {Boolean}
7458      */
7459     isLocked : function(){
7460         return this.locked;
7461     }
7462 });/*
7463  * Based on:
7464  * Ext JS Library 1.1.1
7465  * Copyright(c) 2006-2007, Ext JS, LLC.
7466  *
7467  * Originally Released Under LGPL - original licence link has changed is not relivant.
7468  *
7469  * Fork - LGPL
7470  * <script type="text/javascript">
7471  */
7472 /**
7473  * @extends Roo.grid.AbstractSelectionModel
7474  * @class Roo.grid.RowSelectionModel
7475  * The default SelectionModel used by {@link Roo.grid.Grid}.
7476  * It supports multiple selections and keyboard selection/navigation. 
7477  * @constructor
7478  * @param {Object} config
7479  */
7480 Roo.grid.RowSelectionModel = function(config){
7481     Roo.apply(this, config);
7482     this.selections = new Roo.util.MixedCollection(false, function(o){
7483         return o.id;
7484     });
7485
7486     this.last = false;
7487     this.lastActive = false;
7488
7489     this.addEvents({
7490         /**
7491         * @event selectionchange
7492         * Fires when the selection changes
7493         * @param {SelectionModel} this
7494         */
7495        "selectionchange" : true,
7496        /**
7497         * @event afterselectionchange
7498         * Fires after the selection changes (eg. by key press or clicking)
7499         * @param {SelectionModel} this
7500         */
7501        "afterselectionchange" : true,
7502        /**
7503         * @event beforerowselect
7504         * Fires when a row is selected being selected, return false to cancel.
7505         * @param {SelectionModel} this
7506         * @param {Number} rowIndex The selected index
7507         * @param {Boolean} keepExisting False if other selections will be cleared
7508         */
7509        "beforerowselect" : true,
7510        /**
7511         * @event rowselect
7512         * Fires when a row is selected.
7513         * @param {SelectionModel} this
7514         * @param {Number} rowIndex The selected index
7515         * @param {Roo.data.Record} r The record
7516         */
7517        "rowselect" : true,
7518        /**
7519         * @event rowdeselect
7520         * Fires when a row is deselected.
7521         * @param {SelectionModel} this
7522         * @param {Number} rowIndex The selected index
7523         */
7524         "rowdeselect" : true
7525     });
7526     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7527     this.locked = false;
7528 };
7529
7530 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7531     /**
7532      * @cfg {Boolean} singleSelect
7533      * True to allow selection of only one row at a time (defaults to false)
7534      */
7535     singleSelect : false,
7536
7537     // private
7538     initEvents : function(){
7539
7540         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7541             this.grid.on("mousedown", this.handleMouseDown, this);
7542         }else{ // allow click to work like normal
7543             this.grid.on("rowclick", this.handleDragableRowClick, this);
7544         }
7545         // bootstrap does not have a view..
7546         var view = this.grid.view ? this.grid.view : this.grid;
7547         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7548             "up" : function(e){
7549                 if(!e.shiftKey){
7550                     this.selectPrevious(e.shiftKey);
7551                 }else if(this.last !== false && this.lastActive !== false){
7552                     var last = this.last;
7553                     this.selectRange(this.last,  this.lastActive-1);
7554                     view.focusRow(this.lastActive);
7555                     if(last !== false){
7556                         this.last = last;
7557                     }
7558                 }else{
7559                     this.selectFirstRow();
7560                 }
7561                 this.fireEvent("afterselectionchange", this);
7562             },
7563             "down" : function(e){
7564                 if(!e.shiftKey){
7565                     this.selectNext(e.shiftKey);
7566                 }else if(this.last !== false && this.lastActive !== false){
7567                     var last = this.last;
7568                     this.selectRange(this.last,  this.lastActive+1);
7569                     view.focusRow(this.lastActive);
7570                     if(last !== false){
7571                         this.last = last;
7572                     }
7573                 }else{
7574                     this.selectFirstRow();
7575                 }
7576                 this.fireEvent("afterselectionchange", this);
7577             },
7578             scope: this
7579         });
7580
7581          
7582         view.on("refresh", this.onRefresh, this);
7583         view.on("rowupdated", this.onRowUpdated, this);
7584         view.on("rowremoved", this.onRemove, this);
7585     },
7586
7587     // private
7588     onRefresh : function(){
7589         var ds = this.grid.ds, i, v = this.grid.view;
7590         var s = this.selections;
7591         s.each(function(r){
7592             if((i = ds.indexOfId(r.id)) != -1){
7593                 v.onRowSelect(i);
7594                 s.add(ds.getAt(i)); // updating the selection relate data
7595             }else{
7596                 s.remove(r);
7597             }
7598         });
7599     },
7600
7601     // private
7602     onRemove : function(v, index, r){
7603         this.selections.remove(r);
7604     },
7605
7606     // private
7607     onRowUpdated : function(v, index, r){
7608         if(this.isSelected(r)){
7609             v.onRowSelect(index);
7610         }
7611     },
7612
7613     /**
7614      * Select records.
7615      * @param {Array} records The records to select
7616      * @param {Boolean} keepExisting (optional) True to keep existing selections
7617      */
7618     selectRecords : function(records, keepExisting){
7619         if(!keepExisting){
7620             this.clearSelections();
7621         }
7622         var ds = this.grid.ds;
7623         for(var i = 0, len = records.length; i < len; i++){
7624             this.selectRow(ds.indexOf(records[i]), true);
7625         }
7626     },
7627
7628     /**
7629      * Gets the number of selected rows.
7630      * @return {Number}
7631      */
7632     getCount : function(){
7633         return this.selections.length;
7634     },
7635
7636     /**
7637      * Selects the first row in the grid.
7638      */
7639     selectFirstRow : function(){
7640         this.selectRow(0);
7641     },
7642
7643     /**
7644      * Select the last row.
7645      * @param {Boolean} keepExisting (optional) True to keep existing selections
7646      */
7647     selectLastRow : function(keepExisting){
7648         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7649     },
7650
7651     /**
7652      * Selects the row immediately following the last selected row.
7653      * @param {Boolean} keepExisting (optional) True to keep existing selections
7654      */
7655     selectNext : function(keepExisting){
7656         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7657             this.selectRow(this.last+1, keepExisting);
7658             var view = this.grid.view ? this.grid.view : this.grid;
7659             view.focusRow(this.last);
7660         }
7661     },
7662
7663     /**
7664      * Selects the row that precedes the last selected row.
7665      * @param {Boolean} keepExisting (optional) True to keep existing selections
7666      */
7667     selectPrevious : function(keepExisting){
7668         if(this.last){
7669             this.selectRow(this.last-1, keepExisting);
7670             var view = this.grid.view ? this.grid.view : this.grid;
7671             view.focusRow(this.last);
7672         }
7673     },
7674
7675     /**
7676      * Returns the selected records
7677      * @return {Array} Array of selected records
7678      */
7679     getSelections : function(){
7680         return [].concat(this.selections.items);
7681     },
7682
7683     /**
7684      * Returns the first selected record.
7685      * @return {Record}
7686      */
7687     getSelected : function(){
7688         return this.selections.itemAt(0);
7689     },
7690
7691
7692     /**
7693      * Clears all selections.
7694      */
7695     clearSelections : function(fast){
7696         if(this.locked) {
7697             return;
7698         }
7699         if(fast !== true){
7700             var ds = this.grid.ds;
7701             var s = this.selections;
7702             s.each(function(r){
7703                 this.deselectRow(ds.indexOfId(r.id));
7704             }, this);
7705             s.clear();
7706         }else{
7707             this.selections.clear();
7708         }
7709         this.last = false;
7710     },
7711
7712
7713     /**
7714      * Selects all rows.
7715      */
7716     selectAll : function(){
7717         if(this.locked) {
7718             return;
7719         }
7720         this.selections.clear();
7721         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7722             this.selectRow(i, true);
7723         }
7724     },
7725
7726     /**
7727      * Returns True if there is a selection.
7728      * @return {Boolean}
7729      */
7730     hasSelection : function(){
7731         return this.selections.length > 0;
7732     },
7733
7734     /**
7735      * Returns True if the specified row is selected.
7736      * @param {Number/Record} record The record or index of the record to check
7737      * @return {Boolean}
7738      */
7739     isSelected : function(index){
7740         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7741         return (r && this.selections.key(r.id) ? true : false);
7742     },
7743
7744     /**
7745      * Returns True if the specified record id is selected.
7746      * @param {String} id The id of record to check
7747      * @return {Boolean}
7748      */
7749     isIdSelected : function(id){
7750         return (this.selections.key(id) ? true : false);
7751     },
7752
7753     // private
7754     handleMouseDown : function(e, t)
7755     {
7756         var view = this.grid.view ? this.grid.view : this.grid;
7757         var rowIndex;
7758         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7759             return;
7760         };
7761         if(e.shiftKey && this.last !== false){
7762             var last = this.last;
7763             this.selectRange(last, rowIndex, e.ctrlKey);
7764             this.last = last; // reset the last
7765             view.focusRow(rowIndex);
7766         }else{
7767             var isSelected = this.isSelected(rowIndex);
7768             if(e.button !== 0 && isSelected){
7769                 view.focusRow(rowIndex);
7770             }else if(e.ctrlKey && isSelected){
7771                 this.deselectRow(rowIndex);
7772             }else if(!isSelected){
7773                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7774                 view.focusRow(rowIndex);
7775             }
7776         }
7777         this.fireEvent("afterselectionchange", this);
7778     },
7779     // private
7780     handleDragableRowClick :  function(grid, rowIndex, e) 
7781     {
7782         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7783             this.selectRow(rowIndex, false);
7784             var view = this.grid.view ? this.grid.view : this.grid;
7785             view.focusRow(rowIndex);
7786              this.fireEvent("afterselectionchange", this);
7787         }
7788     },
7789     
7790     /**
7791      * Selects multiple rows.
7792      * @param {Array} rows Array of the indexes of the row to select
7793      * @param {Boolean} keepExisting (optional) True to keep existing selections
7794      */
7795     selectRows : function(rows, keepExisting){
7796         if(!keepExisting){
7797             this.clearSelections();
7798         }
7799         for(var i = 0, len = rows.length; i < len; i++){
7800             this.selectRow(rows[i], true);
7801         }
7802     },
7803
7804     /**
7805      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7806      * @param {Number} startRow The index of the first row in the range
7807      * @param {Number} endRow The index of the last row in the range
7808      * @param {Boolean} keepExisting (optional) True to retain existing selections
7809      */
7810     selectRange : function(startRow, endRow, keepExisting){
7811         if(this.locked) {
7812             return;
7813         }
7814         if(!keepExisting){
7815             this.clearSelections();
7816         }
7817         if(startRow <= endRow){
7818             for(var i = startRow; i <= endRow; i++){
7819                 this.selectRow(i, true);
7820             }
7821         }else{
7822             for(var i = startRow; i >= endRow; i--){
7823                 this.selectRow(i, true);
7824             }
7825         }
7826     },
7827
7828     /**
7829      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7830      * @param {Number} startRow The index of the first row in the range
7831      * @param {Number} endRow The index of the last row in the range
7832      */
7833     deselectRange : function(startRow, endRow, preventViewNotify){
7834         if(this.locked) {
7835             return;
7836         }
7837         for(var i = startRow; i <= endRow; i++){
7838             this.deselectRow(i, preventViewNotify);
7839         }
7840     },
7841
7842     /**
7843      * Selects a row.
7844      * @param {Number} row The index of the row to select
7845      * @param {Boolean} keepExisting (optional) True to keep existing selections
7846      */
7847     selectRow : function(index, keepExisting, preventViewNotify){
7848         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7849             return;
7850         }
7851         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7852             if(!keepExisting || this.singleSelect){
7853                 this.clearSelections();
7854             }
7855             var r = this.grid.ds.getAt(index);
7856             this.selections.add(r);
7857             this.last = this.lastActive = index;
7858             if(!preventViewNotify){
7859                 var view = this.grid.view ? this.grid.view : this.grid;
7860                 view.onRowSelect(index);
7861             }
7862             this.fireEvent("rowselect", this, index, r);
7863             this.fireEvent("selectionchange", this);
7864         }
7865     },
7866
7867     /**
7868      * Deselects a row.
7869      * @param {Number} row The index of the row to deselect
7870      */
7871     deselectRow : function(index, preventViewNotify){
7872         if(this.locked) {
7873             return;
7874         }
7875         if(this.last == index){
7876             this.last = false;
7877         }
7878         if(this.lastActive == index){
7879             this.lastActive = false;
7880         }
7881         var r = this.grid.ds.getAt(index);
7882         this.selections.remove(r);
7883         if(!preventViewNotify){
7884             var view = this.grid.view ? this.grid.view : this.grid;
7885             view.onRowDeselect(index);
7886         }
7887         this.fireEvent("rowdeselect", this, index);
7888         this.fireEvent("selectionchange", this);
7889     },
7890
7891     // private
7892     restoreLast : function(){
7893         if(this._last){
7894             this.last = this._last;
7895         }
7896     },
7897
7898     // private
7899     acceptsNav : function(row, col, cm){
7900         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7901     },
7902
7903     // private
7904     onEditorKey : function(field, e){
7905         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7906         if(k == e.TAB){
7907             e.stopEvent();
7908             ed.completeEdit();
7909             if(e.shiftKey){
7910                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7911             }else{
7912                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7913             }
7914         }else if(k == e.ENTER && !e.ctrlKey){
7915             e.stopEvent();
7916             ed.completeEdit();
7917             if(e.shiftKey){
7918                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7919             }else{
7920                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7921             }
7922         }else if(k == e.ESC){
7923             ed.cancelEdit();
7924         }
7925         if(newCell){
7926             g.startEditing(newCell[0], newCell[1]);
7927         }
7928     }
7929 });/*
7930  * Based on:
7931  * Ext JS Library 1.1.1
7932  * Copyright(c) 2006-2007, Ext JS, LLC.
7933  *
7934  * Originally Released Under LGPL - original licence link has changed is not relivant.
7935  *
7936  * Fork - LGPL
7937  * <script type="text/javascript">
7938  */
7939  
7940
7941 /**
7942  * @class Roo.grid.ColumnModel
7943  * @extends Roo.util.Observable
7944  * This is the default implementation of a ColumnModel used by the Grid. It defines
7945  * the columns in the grid.
7946  * <br>Usage:<br>
7947  <pre><code>
7948  var colModel = new Roo.grid.ColumnModel([
7949         {header: "Ticker", width: 60, sortable: true, locked: true},
7950         {header: "Company Name", width: 150, sortable: true},
7951         {header: "Market Cap.", width: 100, sortable: true},
7952         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7953         {header: "Employees", width: 100, sortable: true, resizable: false}
7954  ]);
7955  </code></pre>
7956  * <p>
7957  
7958  * The config options listed for this class are options which may appear in each
7959  * individual column definition.
7960  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7961  * @constructor
7962  * @param {Object} config An Array of column config objects. See this class's
7963  * config objects for details.
7964 */
7965 Roo.grid.ColumnModel = function(config){
7966         /**
7967      * The config passed into the constructor
7968      */
7969     this.config = []; //config;
7970     this.lookup = {};
7971
7972     // if no id, create one
7973     // if the column does not have a dataIndex mapping,
7974     // map it to the order it is in the config
7975     for(var i = 0, len = config.length; i < len; i++){
7976         this.addColumn(config[i]);
7977         
7978     }
7979
7980     /**
7981      * The width of columns which have no width specified (defaults to 100)
7982      * @type Number
7983      */
7984     this.defaultWidth = 100;
7985
7986     /**
7987      * Default sortable of columns which have no sortable specified (defaults to false)
7988      * @type Boolean
7989      */
7990     this.defaultSortable = false;
7991
7992     this.addEvents({
7993         /**
7994              * @event widthchange
7995              * Fires when the width of a column changes.
7996              * @param {ColumnModel} this
7997              * @param {Number} columnIndex The column index
7998              * @param {Number} newWidth The new width
7999              */
8000             "widthchange": true,
8001         /**
8002              * @event headerchange
8003              * Fires when the text of a header changes.
8004              * @param {ColumnModel} this
8005              * @param {Number} columnIndex The column index
8006              * @param {Number} newText The new header text
8007              */
8008             "headerchange": true,
8009         /**
8010              * @event hiddenchange
8011              * Fires when a column is hidden or "unhidden".
8012              * @param {ColumnModel} this
8013              * @param {Number} columnIndex The column index
8014              * @param {Boolean} hidden true if hidden, false otherwise
8015              */
8016             "hiddenchange": true,
8017             /**
8018          * @event columnmoved
8019          * Fires when a column is moved.
8020          * @param {ColumnModel} this
8021          * @param {Number} oldIndex
8022          * @param {Number} newIndex
8023          */
8024         "columnmoved" : true,
8025         /**
8026          * @event columlockchange
8027          * Fires when a column's locked state is changed
8028          * @param {ColumnModel} this
8029          * @param {Number} colIndex
8030          * @param {Boolean} locked true if locked
8031          */
8032         "columnlockchange" : true
8033     });
8034     Roo.grid.ColumnModel.superclass.constructor.call(this);
8035 };
8036 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8037     /**
8038      * @cfg {String} header The header text to display in the Grid view.
8039      */
8040         /**
8041      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8042      */
8043         /**
8044      * @cfg {String} smHeader Header at Bootsrap Small width
8045      */
8046         /**
8047      * @cfg {String} mdHeader Header at Bootsrap Medium width
8048      */
8049         /**
8050      * @cfg {String} lgHeader Header at Bootsrap Large width
8051      */
8052         /**
8053      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8054      */
8055     /**
8056      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8057      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8058      * specified, the column's index is used as an index into the Record's data Array.
8059      */
8060     /**
8061      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8062      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8063      */
8064     /**
8065      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8066      * Defaults to the value of the {@link #defaultSortable} property.
8067      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8068      */
8069     /**
8070      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8071      */
8072     /**
8073      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8074      */
8075     /**
8076      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8077      */
8078     /**
8079      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8080      */
8081     /**
8082      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8083      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8084      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8085      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8086      */
8087        /**
8088      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8089      */
8090     /**
8091      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8092      */
8093     /**
8094      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8095      */
8096     /**
8097      * @cfg {String} cursor (Optional)
8098      */
8099     /**
8100      * @cfg {String} tooltip (Optional)
8101      */
8102     /**
8103      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8104      */
8105     /**
8106      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111     /**
8112      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114         /**
8115      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8116      */
8117     /**
8118      * Returns the id of the column at the specified index.
8119      * @param {Number} index The column index
8120      * @return {String} the id
8121      */
8122     getColumnId : function(index){
8123         return this.config[index].id;
8124     },
8125
8126     /**
8127      * Returns the column for a specified id.
8128      * @param {String} id The column id
8129      * @return {Object} the column
8130      */
8131     getColumnById : function(id){
8132         return this.lookup[id];
8133     },
8134
8135     
8136     /**
8137      * Returns the column Object for a specified dataIndex.
8138      * @param {String} dataIndex The column dataIndex
8139      * @return {Object|Boolean} the column or false if not found
8140      */
8141     getColumnByDataIndex: function(dataIndex){
8142         var index = this.findColumnIndex(dataIndex);
8143         return index > -1 ? this.config[index] : false;
8144     },
8145     
8146     /**
8147      * Returns the index for a specified column id.
8148      * @param {String} id The column id
8149      * @return {Number} the index, or -1 if not found
8150      */
8151     getIndexById : function(id){
8152         for(var i = 0, len = this.config.length; i < len; i++){
8153             if(this.config[i].id == id){
8154                 return i;
8155             }
8156         }
8157         return -1;
8158     },
8159     
8160     /**
8161      * Returns the index for a specified column dataIndex.
8162      * @param {String} dataIndex The column dataIndex
8163      * @return {Number} the index, or -1 if not found
8164      */
8165     
8166     findColumnIndex : function(dataIndex){
8167         for(var i = 0, len = this.config.length; i < len; i++){
8168             if(this.config[i].dataIndex == dataIndex){
8169                 return i;
8170             }
8171         }
8172         return -1;
8173     },
8174     
8175     
8176     moveColumn : function(oldIndex, newIndex){
8177         var c = this.config[oldIndex];
8178         this.config.splice(oldIndex, 1);
8179         this.config.splice(newIndex, 0, c);
8180         this.dataMap = null;
8181         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8182     },
8183
8184     isLocked : function(colIndex){
8185         return this.config[colIndex].locked === true;
8186     },
8187
8188     setLocked : function(colIndex, value, suppressEvent){
8189         if(this.isLocked(colIndex) == value){
8190             return;
8191         }
8192         this.config[colIndex].locked = value;
8193         if(!suppressEvent){
8194             this.fireEvent("columnlockchange", this, colIndex, value);
8195         }
8196     },
8197
8198     getTotalLockedWidth : function(){
8199         var totalWidth = 0;
8200         for(var i = 0; i < this.config.length; i++){
8201             if(this.isLocked(i) && !this.isHidden(i)){
8202                 this.totalWidth += this.getColumnWidth(i);
8203             }
8204         }
8205         return totalWidth;
8206     },
8207
8208     getLockedCount : function(){
8209         for(var i = 0, len = this.config.length; i < len; i++){
8210             if(!this.isLocked(i)){
8211                 return i;
8212             }
8213         }
8214         
8215         return this.config.length;
8216     },
8217
8218     /**
8219      * Returns the number of columns.
8220      * @return {Number}
8221      */
8222     getColumnCount : function(visibleOnly){
8223         if(visibleOnly === true){
8224             var c = 0;
8225             for(var i = 0, len = this.config.length; i < len; i++){
8226                 if(!this.isHidden(i)){
8227                     c++;
8228                 }
8229             }
8230             return c;
8231         }
8232         return this.config.length;
8233     },
8234
8235     /**
8236      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8237      * @param {Function} fn
8238      * @param {Object} scope (optional)
8239      * @return {Array} result
8240      */
8241     getColumnsBy : function(fn, scope){
8242         var r = [];
8243         for(var i = 0, len = this.config.length; i < len; i++){
8244             var c = this.config[i];
8245             if(fn.call(scope||this, c, i) === true){
8246                 r[r.length] = c;
8247             }
8248         }
8249         return r;
8250     },
8251
8252     /**
8253      * Returns true if the specified column is sortable.
8254      * @param {Number} col The column index
8255      * @return {Boolean}
8256      */
8257     isSortable : function(col){
8258         if(typeof this.config[col].sortable == "undefined"){
8259             return this.defaultSortable;
8260         }
8261         return this.config[col].sortable;
8262     },
8263
8264     /**
8265      * Returns the rendering (formatting) function defined for the column.
8266      * @param {Number} col The column index.
8267      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8268      */
8269     getRenderer : function(col){
8270         if(!this.config[col].renderer){
8271             return Roo.grid.ColumnModel.defaultRenderer;
8272         }
8273         return this.config[col].renderer;
8274     },
8275
8276     /**
8277      * Sets the rendering (formatting) function for a column.
8278      * @param {Number} col The column index
8279      * @param {Function} fn The function to use to process the cell's raw data
8280      * to return HTML markup for the grid view. The render function is called with
8281      * the following parameters:<ul>
8282      * <li>Data value.</li>
8283      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8284      * <li>css A CSS style string to apply to the table cell.</li>
8285      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8286      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8287      * <li>Row index</li>
8288      * <li>Column index</li>
8289      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8290      */
8291     setRenderer : function(col, fn){
8292         this.config[col].renderer = fn;
8293     },
8294
8295     /**
8296      * Returns the width for the specified column.
8297      * @param {Number} col The column index
8298      * @param (optional) {String} gridSize bootstrap width size.
8299      * @return {Number}
8300      */
8301     getColumnWidth : function(col, gridSize)
8302         {
8303                 var cfg = this.config[col];
8304                 
8305                 if (typeof(gridSize) == 'undefined') {
8306                         return cfg.width * 1 || this.defaultWidth;
8307                 }
8308                 if (gridSize === false) { // if we set it..
8309                         return cfg.width || false;
8310                 }
8311                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8312                 
8313                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8314                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8315                                 continue;
8316                         }
8317                         return cfg[ sizes[i] ];
8318                 }
8319                 return 1;
8320                 
8321     },
8322
8323     /**
8324      * Sets the width for a column.
8325      * @param {Number} col The column index
8326      * @param {Number} width The new width
8327      */
8328     setColumnWidth : function(col, width, suppressEvent){
8329         this.config[col].width = width;
8330         this.totalWidth = null;
8331         if(!suppressEvent){
8332              this.fireEvent("widthchange", this, col, width);
8333         }
8334     },
8335
8336     /**
8337      * Returns the total width of all columns.
8338      * @param {Boolean} includeHidden True to include hidden column widths
8339      * @return {Number}
8340      */
8341     getTotalWidth : function(includeHidden){
8342         if(!this.totalWidth){
8343             this.totalWidth = 0;
8344             for(var i = 0, len = this.config.length; i < len; i++){
8345                 if(includeHidden || !this.isHidden(i)){
8346                     this.totalWidth += this.getColumnWidth(i);
8347                 }
8348             }
8349         }
8350         return this.totalWidth;
8351     },
8352
8353     /**
8354      * Returns the header for the specified column.
8355      * @param {Number} col The column index
8356      * @return {String}
8357      */
8358     getColumnHeader : function(col){
8359         return this.config[col].header;
8360     },
8361
8362     /**
8363      * Sets the header for a column.
8364      * @param {Number} col The column index
8365      * @param {String} header The new header
8366      */
8367     setColumnHeader : function(col, header){
8368         this.config[col].header = header;
8369         this.fireEvent("headerchange", this, col, header);
8370     },
8371
8372     /**
8373      * Returns the tooltip for the specified column.
8374      * @param {Number} col The column index
8375      * @return {String}
8376      */
8377     getColumnTooltip : function(col){
8378             return this.config[col].tooltip;
8379     },
8380     /**
8381      * Sets the tooltip for a column.
8382      * @param {Number} col The column index
8383      * @param {String} tooltip The new tooltip
8384      */
8385     setColumnTooltip : function(col, tooltip){
8386             this.config[col].tooltip = tooltip;
8387     },
8388
8389     /**
8390      * Returns the dataIndex for the specified column.
8391      * @param {Number} col The column index
8392      * @return {Number}
8393      */
8394     getDataIndex : function(col){
8395         return this.config[col].dataIndex;
8396     },
8397
8398     /**
8399      * Sets the dataIndex for a column.
8400      * @param {Number} col The column index
8401      * @param {Number} dataIndex The new dataIndex
8402      */
8403     setDataIndex : function(col, dataIndex){
8404         this.config[col].dataIndex = dataIndex;
8405     },
8406
8407     
8408     
8409     /**
8410      * Returns true if the cell is editable.
8411      * @param {Number} colIndex The column index
8412      * @param {Number} rowIndex The row index - this is nto actually used..?
8413      * @return {Boolean}
8414      */
8415     isCellEditable : function(colIndex, rowIndex){
8416         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8417     },
8418
8419     /**
8420      * Returns the editor defined for the cell/column.
8421      * return false or null to disable editing.
8422      * @param {Number} colIndex The column index
8423      * @param {Number} rowIndex The row index
8424      * @return {Object}
8425      */
8426     getCellEditor : function(colIndex, rowIndex){
8427         return this.config[colIndex].editor;
8428     },
8429
8430     /**
8431      * Sets if a column is editable.
8432      * @param {Number} col The column index
8433      * @param {Boolean} editable True if the column is editable
8434      */
8435     setEditable : function(col, editable){
8436         this.config[col].editable = editable;
8437     },
8438
8439
8440     /**
8441      * Returns true if the column is hidden.
8442      * @param {Number} colIndex The column index
8443      * @return {Boolean}
8444      */
8445     isHidden : function(colIndex){
8446         return this.config[colIndex].hidden;
8447     },
8448
8449
8450     /**
8451      * Returns true if the column width cannot be changed
8452      */
8453     isFixed : function(colIndex){
8454         return this.config[colIndex].fixed;
8455     },
8456
8457     /**
8458      * Returns true if the column can be resized
8459      * @return {Boolean}
8460      */
8461     isResizable : function(colIndex){
8462         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8463     },
8464     /**
8465      * Sets if a column is hidden.
8466      * @param {Number} colIndex The column index
8467      * @param {Boolean} hidden True if the column is hidden
8468      */
8469     setHidden : function(colIndex, hidden){
8470         this.config[colIndex].hidden = hidden;
8471         this.totalWidth = null;
8472         this.fireEvent("hiddenchange", this, colIndex, hidden);
8473     },
8474
8475     /**
8476      * Sets the editor for a column.
8477      * @param {Number} col The column index
8478      * @param {Object} editor The editor object
8479      */
8480     setEditor : function(col, editor){
8481         this.config[col].editor = editor;
8482     },
8483     /**
8484      * Add a column (experimental...) - defaults to adding to the end..
8485      * @param {Object} config 
8486     */
8487     addColumn : function(c)
8488     {
8489     
8490         var i = this.config.length;
8491         this.config[i] = c;
8492         
8493         if(typeof c.dataIndex == "undefined"){
8494             c.dataIndex = i;
8495         }
8496         if(typeof c.renderer == "string"){
8497             c.renderer = Roo.util.Format[c.renderer];
8498         }
8499         if(typeof c.id == "undefined"){
8500             c.id = Roo.id();
8501         }
8502         if(c.editor && c.editor.xtype){
8503             c.editor  = Roo.factory(c.editor, Roo.grid);
8504         }
8505         if(c.editor && c.editor.isFormField){
8506             c.editor = new Roo.grid.GridEditor(c.editor);
8507         }
8508         this.lookup[c.id] = c;
8509     }
8510     
8511 });
8512
8513 Roo.grid.ColumnModel.defaultRenderer = function(value)
8514 {
8515     if(typeof value == "object") {
8516         return value;
8517     }
8518         if(typeof value == "string" && value.length < 1){
8519             return "&#160;";
8520         }
8521     
8522         return String.format("{0}", value);
8523 };
8524
8525 // Alias for backwards compatibility
8526 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8527 /*
8528  * Based on:
8529  * Ext JS Library 1.1.1
8530  * Copyright(c) 2006-2007, Ext JS, LLC.
8531  *
8532  * Originally Released Under LGPL - original licence link has changed is not relivant.
8533  *
8534  * Fork - LGPL
8535  * <script type="text/javascript">
8536  */
8537  
8538 /**
8539  * @class Roo.LoadMask
8540  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8541  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8542  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8543  * element's UpdateManager load indicator and will be destroyed after the initial load.
8544  * @constructor
8545  * Create a new LoadMask
8546  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8547  * @param {Object} config The config object
8548  */
8549 Roo.LoadMask = function(el, config){
8550     this.el = Roo.get(el);
8551     Roo.apply(this, config);
8552     if(this.store){
8553         this.store.on('beforeload', this.onBeforeLoad, this);
8554         this.store.on('load', this.onLoad, this);
8555         this.store.on('loadexception', this.onLoadException, this);
8556         this.removeMask = false;
8557     }else{
8558         var um = this.el.getUpdateManager();
8559         um.showLoadIndicator = false; // disable the default indicator
8560         um.on('beforeupdate', this.onBeforeLoad, this);
8561         um.on('update', this.onLoad, this);
8562         um.on('failure', this.onLoad, this);
8563         this.removeMask = true;
8564     }
8565 };
8566
8567 Roo.LoadMask.prototype = {
8568     /**
8569      * @cfg {Boolean} removeMask
8570      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8571      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8572      */
8573     removeMask : false,
8574     /**
8575      * @cfg {String} msg
8576      * The text to display in a centered loading message box (defaults to 'Loading...')
8577      */
8578     msg : 'Loading...',
8579     /**
8580      * @cfg {String} msgCls
8581      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8582      */
8583     msgCls : 'x-mask-loading',
8584
8585     /**
8586      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8587      * @type Boolean
8588      */
8589     disabled: false,
8590
8591     /**
8592      * Disables the mask to prevent it from being displayed
8593      */
8594     disable : function(){
8595        this.disabled = true;
8596     },
8597
8598     /**
8599      * Enables the mask so that it can be displayed
8600      */
8601     enable : function(){
8602         this.disabled = false;
8603     },
8604     
8605     onLoadException : function()
8606     {
8607         Roo.log(arguments);
8608         
8609         if (typeof(arguments[3]) != 'undefined') {
8610             Roo.MessageBox.alert("Error loading",arguments[3]);
8611         } 
8612         /*
8613         try {
8614             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8615                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8616             }   
8617         } catch(e) {
8618             
8619         }
8620         */
8621     
8622         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8623     },
8624     // private
8625     onLoad : function()
8626     {
8627         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8628     },
8629
8630     // private
8631     onBeforeLoad : function(){
8632         if(!this.disabled){
8633             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8634         }
8635     },
8636
8637     // private
8638     destroy : function(){
8639         if(this.store){
8640             this.store.un('beforeload', this.onBeforeLoad, this);
8641             this.store.un('load', this.onLoad, this);
8642             this.store.un('loadexception', this.onLoadException, this);
8643         }else{
8644             var um = this.el.getUpdateManager();
8645             um.un('beforeupdate', this.onBeforeLoad, this);
8646             um.un('update', this.onLoad, this);
8647             um.un('failure', this.onLoad, this);
8648         }
8649     }
8650 };/**
8651  * @class Roo.bootstrap.Table
8652  * @licence LGBL
8653  * @extends Roo.bootstrap.Component
8654  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8655  * Similar to Roo.grid.Grid
8656  * <pre><code>
8657  var table = Roo.factory({
8658     xtype : 'Table',
8659     xns : Roo.bootstrap,
8660     autoSizeColumns: true,
8661     
8662     
8663     store : {
8664         xtype : 'Store',
8665         xns : Roo.data,
8666         remoteSort : true,
8667         sortInfo : { direction : 'ASC', field: 'name' },
8668         proxy : {
8669            xtype : 'HttpProxy',
8670            xns : Roo.data,
8671            method : 'GET',
8672            url : 'https://example.com/some.data.url.json'
8673         },
8674         reader : {
8675            xtype : 'JsonReader',
8676            xns : Roo.data,
8677            fields : [ 'id', 'name', whatever' ],
8678            id : 'id',
8679            root : 'data'
8680         }
8681     },
8682     cm : [
8683         {
8684             xtype : 'ColumnModel',
8685             xns : Roo.grid,
8686             align : 'center',
8687             cursor : 'pointer',
8688             dataIndex : 'is_in_group',
8689             header : "Name",
8690             sortable : true,
8691             renderer : function(v, x , r) {  
8692             
8693                 return String.format("{0}", v)
8694             }
8695             width : 3
8696         } // more columns..
8697     ],
8698     selModel : {
8699         xtype : 'RowSelectionModel',
8700         xns : Roo.bootstrap.Table
8701         // you can add listeners to catch selection change here....
8702     }
8703      
8704
8705  });
8706  // set any options
8707  grid.render(Roo.get("some-div"));
8708 </code></pre>
8709
8710 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8711
8712
8713
8714  *
8715  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
8716  * @cfg {Roo.data.Store} store The data store to use
8717  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8718  * 
8719  * @cfg {String} cls table class
8720  *
8721  * 
8722  * @cfg {boolean} striped Should the rows be alternative striped
8723  * @cfg {boolean} bordered Add borders to the table
8724  * @cfg {boolean} hover Add hover highlighting
8725  * @cfg {boolean} condensed Format condensed
8726  * @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,
8727  *                also adds table-responsive (see bootstrap docs for details)
8728  * @cfg {Boolean} loadMask (true|false) default false
8729  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8730  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8731  * @cfg {Boolean} rowSelection (true|false) default false
8732  * @cfg {Boolean} cellSelection (true|false) default false
8733  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8734  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8735  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8736  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8737  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8738  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8739  * 
8740  * @constructor
8741  * Create a new Table
8742  * @param {Object} config The config object
8743  */
8744
8745 Roo.bootstrap.Table = function(config)
8746 {
8747     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8748      
8749     // BC...
8750     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8751     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8752     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8753     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8754     
8755     this.view = this; // compat with grid.
8756     
8757     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8758     if (this.sm) {
8759         this.sm.grid = this;
8760         this.selModel = Roo.factory(this.sm, Roo.grid);
8761         this.sm = this.selModel;
8762         this.sm.xmodule = this.xmodule || false;
8763     }
8764     
8765     if (this.cm && typeof(this.cm.config) == 'undefined') {
8766         this.colModel = new Roo.grid.ColumnModel(this.cm);
8767         this.cm = this.colModel;
8768         this.cm.xmodule = this.xmodule || false;
8769     }
8770     if (this.store) {
8771         this.store= Roo.factory(this.store, Roo.data);
8772         this.ds = this.store;
8773         this.ds.xmodule = this.xmodule || false;
8774          
8775     }
8776     if (this.footer && this.store) {
8777         this.footer.dataSource = this.ds;
8778         this.footer = Roo.factory(this.footer);
8779     }
8780     
8781     /** @private */
8782     this.addEvents({
8783         /**
8784          * @event cellclick
8785          * Fires when a cell is clicked
8786          * @param {Roo.bootstrap.Table} this
8787          * @param {Roo.Element} el
8788          * @param {Number} rowIndex
8789          * @param {Number} columnIndex
8790          * @param {Roo.EventObject} e
8791          */
8792         "cellclick" : true,
8793         /**
8794          * @event celldblclick
8795          * Fires when a cell is double clicked
8796          * @param {Roo.bootstrap.Table} this
8797          * @param {Roo.Element} el
8798          * @param {Number} rowIndex
8799          * @param {Number} columnIndex
8800          * @param {Roo.EventObject} e
8801          */
8802         "celldblclick" : true,
8803         /**
8804          * @event rowclick
8805          * Fires when a row is clicked
8806          * @param {Roo.bootstrap.Table} this
8807          * @param {Roo.Element} el
8808          * @param {Number} rowIndex
8809          * @param {Roo.EventObject} e
8810          */
8811         "rowclick" : true,
8812         /**
8813          * @event rowdblclick
8814          * Fires when a row is double clicked
8815          * @param {Roo.bootstrap.Table} this
8816          * @param {Roo.Element} el
8817          * @param {Number} rowIndex
8818          * @param {Roo.EventObject} e
8819          */
8820         "rowdblclick" : true,
8821         /**
8822          * @event mouseover
8823          * Fires when a mouseover occur
8824          * @param {Roo.bootstrap.Table} this
8825          * @param {Roo.Element} el
8826          * @param {Number} rowIndex
8827          * @param {Number} columnIndex
8828          * @param {Roo.EventObject} e
8829          */
8830         "mouseover" : true,
8831         /**
8832          * @event mouseout
8833          * Fires when a mouseout occur
8834          * @param {Roo.bootstrap.Table} this
8835          * @param {Roo.Element} el
8836          * @param {Number} rowIndex
8837          * @param {Number} columnIndex
8838          * @param {Roo.EventObject} e
8839          */
8840         "mouseout" : true,
8841         /**
8842          * @event rowclass
8843          * Fires when a row is rendered, so you can change add a style to it.
8844          * @param {Roo.bootstrap.Table} this
8845          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8846          */
8847         'rowclass' : true,
8848           /**
8849          * @event rowsrendered
8850          * Fires when all the  rows have been rendered
8851          * @param {Roo.bootstrap.Table} this
8852          */
8853         'rowsrendered' : true,
8854         /**
8855          * @event contextmenu
8856          * The raw contextmenu event for the entire grid.
8857          * @param {Roo.EventObject} e
8858          */
8859         "contextmenu" : true,
8860         /**
8861          * @event rowcontextmenu
8862          * Fires when a row is right clicked
8863          * @param {Roo.bootstrap.Table} this
8864          * @param {Number} rowIndex
8865          * @param {Roo.EventObject} e
8866          */
8867         "rowcontextmenu" : true,
8868         /**
8869          * @event cellcontextmenu
8870          * Fires when a cell is right clicked
8871          * @param {Roo.bootstrap.Table} this
8872          * @param {Number} rowIndex
8873          * @param {Number} cellIndex
8874          * @param {Roo.EventObject} e
8875          */
8876          "cellcontextmenu" : true,
8877          /**
8878          * @event headercontextmenu
8879          * Fires when a header is right clicked
8880          * @param {Roo.bootstrap.Table} this
8881          * @param {Number} columnIndex
8882          * @param {Roo.EventObject} e
8883          */
8884         "headercontextmenu" : true,
8885         /**
8886          * @event mousedown
8887          * The raw mousedown event for the entire grid.
8888          * @param {Roo.EventObject} e
8889          */
8890         "mousedown" : true
8891         
8892     });
8893 };
8894
8895 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8896     
8897     cls: false,
8898     
8899     striped : false,
8900     scrollBody : false,
8901     bordered: false,
8902     hover:  false,
8903     condensed : false,
8904     responsive : false,
8905     sm : false,
8906     cm : false,
8907     store : false,
8908     loadMask : false,
8909     footerShow : true,
8910     headerShow : true,
8911     enableColumnResize: true,
8912   
8913     rowSelection : false,
8914     cellSelection : false,
8915     layout : false,
8916
8917     minColumnWidth : 50,
8918     
8919     // Roo.Element - the tbody
8920     bodyEl: false,  // <tbody> Roo.Element - thead element    
8921     headEl: false,  // <thead> Roo.Element - thead element
8922     resizeProxy : false, // proxy element for dragging?
8923
8924
8925     
8926     container: false, // used by gridpanel...
8927     
8928     lazyLoad : false,
8929     
8930     CSS : Roo.util.CSS,
8931     
8932     auto_hide_footer : false,
8933     
8934     view: false, // actually points to this..
8935     
8936     getAutoCreate : function()
8937     {
8938         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8939         
8940         cfg = {
8941             tag: 'table',
8942             cls : 'table', 
8943             cn : []
8944         };
8945         // this get's auto added by panel.Grid
8946         if (this.scrollBody) {
8947             cfg.cls += ' table-body-fixed';
8948         }    
8949         if (this.striped) {
8950             cfg.cls += ' table-striped';
8951         }
8952         
8953         if (this.hover) {
8954             cfg.cls += ' table-hover';
8955         }
8956         if (this.bordered) {
8957             cfg.cls += ' table-bordered';
8958         }
8959         if (this.condensed) {
8960             cfg.cls += ' table-condensed';
8961         }
8962         
8963         if (this.responsive) {
8964             cfg.cls += ' table-responsive';
8965         }
8966         
8967         if (this.cls) {
8968             cfg.cls+=  ' ' +this.cls;
8969         }
8970         
8971         
8972         
8973         if (this.layout) {
8974             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8975         }
8976         
8977         if(this.store || this.cm){
8978             if(this.headerShow){
8979                 cfg.cn.push(this.renderHeader());
8980             }
8981             
8982             cfg.cn.push(this.renderBody());
8983             
8984             if(this.footerShow){
8985                 cfg.cn.push(this.renderFooter());
8986             }
8987             // where does this come from?
8988             //cfg.cls+=  ' TableGrid';
8989         }
8990         
8991         return { cn : [ cfg ] };
8992     },
8993     
8994     initEvents : function()
8995     {   
8996         if(!this.store || !this.cm){
8997             return;
8998         }
8999         if (this.selModel) {
9000             this.selModel.initEvents();
9001         }
9002         
9003         
9004         //Roo.log('initEvents with ds!!!!');
9005         
9006         this.bodyEl = this.el.select('tbody', true).first();
9007         this.headEl = this.el.select('thead', true).first();
9008         this.mainFoot = this.el.select('tfoot', true).first();
9009         
9010         
9011         
9012         
9013         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9014             e.on('click', this.sort, this);
9015         }, this);
9016         
9017         
9018         // why is this done????? = it breaks dialogs??
9019         //this.parent().el.setStyle('position', 'relative');
9020         
9021         
9022         if (this.footer) {
9023             this.footer.parentId = this.id;
9024             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9025             
9026             if(this.lazyLoad){
9027                 this.el.select('tfoot tr td').first().addClass('hide');
9028             }
9029         } 
9030         
9031         if(this.loadMask) {
9032             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9033         }
9034         
9035         this.store.on('load', this.onLoad, this);
9036         this.store.on('beforeload', this.onBeforeLoad, this);
9037         this.store.on('update', this.onUpdate, this);
9038         this.store.on('add', this.onAdd, this);
9039         this.store.on("clear", this.clear, this);
9040         
9041         this.el.on("contextmenu", this.onContextMenu, this);
9042         
9043         
9044         this.cm.on("headerchange", this.onHeaderChange, this);
9045         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9046
9047  //?? does bodyEl get replaced on render?
9048         this.bodyEl.on("click", this.onClick, this);
9049         this.bodyEl.on("dblclick", this.onDblClick, this);        
9050         this.bodyEl.on('scroll', this.onBodyScroll, this);
9051
9052         // guessing mainbody will work - this relays usually caught by selmodel at present.
9053         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9054   
9055   
9056         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9057         
9058   
9059         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9060             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9061         }
9062         
9063         this.initCSS();
9064     },
9065     // Compatibility with grid - we implement all the view features at present.
9066     getView : function()
9067     {
9068         return this;
9069     },
9070     
9071     initCSS : function()
9072     {
9073         
9074         
9075         var cm = this.cm, styles = [];
9076         this.CSS.removeStyleSheet(this.id + '-cssrules');
9077         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9078         // we can honour xs/sm/md/xl  as widths...
9079         // we first have to decide what widht we are currently at...
9080         var sz = Roo.getGridSize();
9081         
9082         var total = 0;
9083         var last = -1;
9084         var cols = []; // visable cols.
9085         var total_abs = 0;
9086         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9087             var w = cm.getColumnWidth(i, false);
9088             if(cm.isHidden(i)){
9089                 cols.push( { rel : false, abs : 0 });
9090                 continue;
9091             }
9092             if (w !== false) {
9093                 cols.push( { rel : false, abs : w });
9094                 total_abs += w;
9095                 last = i; // not really..
9096                 continue;
9097             }
9098             var w = cm.getColumnWidth(i, sz);
9099             if (w > 0) {
9100                 last = i
9101             }
9102             total += w;
9103             cols.push( { rel : w, abs : false });
9104         }
9105         
9106         var avail = this.bodyEl.dom.clientWidth - total_abs;
9107         
9108         var unitWidth = Math.floor(avail / total);
9109         var rem = avail - (unitWidth * total);
9110         
9111         var hidden, width, pos = 0 , splithide , left;
9112         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9113             
9114             hidden = 'display:none;';
9115             left = '';
9116             width  = 'width:0px;';
9117             splithide = '';
9118             if(!cm.isHidden(i)){
9119                 hidden = '';
9120                 
9121                 
9122                 // we can honour xs/sm/md/xl ?
9123                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9124                 if (w===0) {
9125                     hidden = 'display:none;';
9126                 }
9127                 // width should return a small number...
9128                 if (i == last) {
9129                     w+=rem; // add the remaining with..
9130                 }
9131                 pos += w;
9132                 left = "left:" + (pos -4) + "px;";
9133                 width = "width:" + w+ "px;";
9134                 
9135             }
9136             if (this.responsive) {
9137                 width = '';
9138                 left = '';
9139                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9140                 splithide = 'display: none;';
9141             }
9142             
9143             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9144             if (this.headEl) {
9145                 if (i == last) {
9146                     splithide = 'display:none;';
9147                 }
9148                 
9149                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9150                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9151                 );
9152             }
9153             
9154         }
9155         //Roo.log(styles.join(''));
9156         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9157         
9158     },
9159     
9160     
9161     
9162     onContextMenu : function(e, t)
9163     {
9164         this.processEvent("contextmenu", e);
9165     },
9166     
9167     processEvent : function(name, e)
9168     {
9169         if (name != 'touchstart' ) {
9170             this.fireEvent(name, e);    
9171         }
9172         
9173         var t = e.getTarget();
9174         
9175         var cell = Roo.get(t);
9176         
9177         if(!cell){
9178             return;
9179         }
9180         
9181         if(cell.findParent('tfoot', false, true)){
9182             return;
9183         }
9184         
9185         if(cell.findParent('thead', false, true)){
9186             
9187             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9188                 cell = Roo.get(t).findParent('th', false, true);
9189                 if (!cell) {
9190                     Roo.log("failed to find th in thead?");
9191                     Roo.log(e.getTarget());
9192                     return;
9193                 }
9194             }
9195             
9196             var cellIndex = cell.dom.cellIndex;
9197             
9198             var ename = name == 'touchstart' ? 'click' : name;
9199             this.fireEvent("header" + ename, this, cellIndex, e);
9200             
9201             return;
9202         }
9203         
9204         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9205             cell = Roo.get(t).findParent('td', false, true);
9206             if (!cell) {
9207                 Roo.log("failed to find th in tbody?");
9208                 Roo.log(e.getTarget());
9209                 return;
9210             }
9211         }
9212         
9213         var row = cell.findParent('tr', false, true);
9214         var cellIndex = cell.dom.cellIndex;
9215         var rowIndex = row.dom.rowIndex - 1;
9216         
9217         if(row !== false){
9218             
9219             this.fireEvent("row" + name, this, rowIndex, e);
9220             
9221             if(cell !== false){
9222             
9223                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9224             }
9225         }
9226         
9227     },
9228     
9229     onMouseover : function(e, el)
9230     {
9231         var cell = Roo.get(el);
9232         
9233         if(!cell){
9234             return;
9235         }
9236         
9237         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9238             cell = cell.findParent('td', false, true);
9239         }
9240         
9241         var row = cell.findParent('tr', false, true);
9242         var cellIndex = cell.dom.cellIndex;
9243         var rowIndex = row.dom.rowIndex - 1; // start from 0
9244         
9245         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9246         
9247     },
9248     
9249     onMouseout : function(e, el)
9250     {
9251         var cell = Roo.get(el);
9252         
9253         if(!cell){
9254             return;
9255         }
9256         
9257         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9258             cell = cell.findParent('td', false, true);
9259         }
9260         
9261         var row = cell.findParent('tr', false, true);
9262         var cellIndex = cell.dom.cellIndex;
9263         var rowIndex = row.dom.rowIndex - 1; // start from 0
9264         
9265         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9266         
9267     },
9268     
9269     onClick : function(e, el)
9270     {
9271         var cell = Roo.get(el);
9272         
9273         if(!cell || (!this.cellSelection && !this.rowSelection)){
9274             return;
9275         }
9276         
9277         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9278             cell = cell.findParent('td', false, true);
9279         }
9280         
9281         if(!cell || typeof(cell) == 'undefined'){
9282             return;
9283         }
9284         
9285         var row = cell.findParent('tr', false, true);
9286         
9287         if(!row || typeof(row) == 'undefined'){
9288             return;
9289         }
9290         
9291         var cellIndex = cell.dom.cellIndex;
9292         var rowIndex = this.getRowIndex(row);
9293         
9294         // why??? - should these not be based on SelectionModel?
9295         //if(this.cellSelection){
9296             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9297         //}
9298         
9299         //if(this.rowSelection){
9300             this.fireEvent('rowclick', this, row, rowIndex, e);
9301         //}
9302          
9303     },
9304         
9305     onDblClick : function(e,el)
9306     {
9307         var cell = Roo.get(el);
9308         
9309         if(!cell || (!this.cellSelection && !this.rowSelection)){
9310             return;
9311         }
9312         
9313         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9314             cell = cell.findParent('td', false, true);
9315         }
9316         
9317         if(!cell || typeof(cell) == 'undefined'){
9318             return;
9319         }
9320         
9321         var row = cell.findParent('tr', false, true);
9322         
9323         if(!row || typeof(row) == 'undefined'){
9324             return;
9325         }
9326         
9327         var cellIndex = cell.dom.cellIndex;
9328         var rowIndex = this.getRowIndex(row);
9329         
9330         if(this.cellSelection){
9331             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9332         }
9333         
9334         if(this.rowSelection){
9335             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9336         }
9337     },
9338     findRowIndex : function(el)
9339     {
9340         var cell = Roo.get(el);
9341         if(!cell) {
9342             return false;
9343         }
9344         var row = cell.findParent('tr', false, true);
9345         
9346         if(!row || typeof(row) == 'undefined'){
9347             return false;
9348         }
9349         return this.getRowIndex(row);
9350     },
9351     sort : function(e,el)
9352     {
9353         var col = Roo.get(el);
9354         
9355         if(!col.hasClass('sortable')){
9356             return;
9357         }
9358         
9359         var sort = col.attr('sort');
9360         var dir = 'ASC';
9361         
9362         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9363             dir = 'DESC';
9364         }
9365         
9366         this.store.sortInfo = {field : sort, direction : dir};
9367         
9368         if (this.footer) {
9369             Roo.log("calling footer first");
9370             this.footer.onClick('first');
9371         } else {
9372         
9373             this.store.load({ params : { start : 0 } });
9374         }
9375     },
9376     
9377     renderHeader : function()
9378     {
9379         var header = {
9380             tag: 'thead',
9381             cn : []
9382         };
9383         
9384         var cm = this.cm;
9385         this.totalWidth = 0;
9386         
9387         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9388             
9389             var config = cm.config[i];
9390             
9391             var c = {
9392                 tag: 'th',
9393                 cls : 'x-hcol-' + i,
9394                 style : '',
9395                 
9396                 html: cm.getColumnHeader(i)
9397             };
9398             
9399             var tooltip = cm.getColumnTooltip(i);
9400             if (tooltip) {
9401                 c.tooltip = tooltip;
9402             }
9403             
9404             
9405             var hh = '';
9406             
9407             if(typeof(config.sortable) != 'undefined' && config.sortable){
9408                 c.cls += ' sortable';
9409                 c.html = '<i class="fa"></i>' + c.html;
9410             }
9411             
9412             // could use BS4 hidden-..-down 
9413             
9414             if(typeof(config.lgHeader) != 'undefined'){
9415                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9416             }
9417             
9418             if(typeof(config.mdHeader) != 'undefined'){
9419                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9420             }
9421             
9422             if(typeof(config.smHeader) != 'undefined'){
9423                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9424             }
9425             
9426             if(typeof(config.xsHeader) != 'undefined'){
9427                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9428             }
9429             
9430             if(hh.length){
9431                 c.html = hh;
9432             }
9433             
9434             if(typeof(config.tooltip) != 'undefined'){
9435                 c.tooltip = config.tooltip;
9436             }
9437             
9438             if(typeof(config.colspan) != 'undefined'){
9439                 c.colspan = config.colspan;
9440             }
9441             
9442             // hidden is handled by CSS now
9443             
9444             if(typeof(config.dataIndex) != 'undefined'){
9445                 c.sort = config.dataIndex;
9446             }
9447             
9448            
9449             
9450             if(typeof(config.align) != 'undefined' && config.align.length){
9451                 c.style += ' text-align:' + config.align + ';';
9452             }
9453             
9454             /* width is done in CSS
9455              *if(typeof(config.width) != 'undefined'){
9456                 c.style += ' width:' + config.width + 'px;';
9457                 this.totalWidth += config.width;
9458             } else {
9459                 this.totalWidth += 100; // assume minimum of 100 per column?
9460             }
9461             */
9462             
9463             if(typeof(config.cls) != 'undefined'){
9464                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9465             }
9466             // this is the bit that doesnt reall work at all...
9467             
9468             if (this.responsive) {
9469                  
9470             
9471                 ['xs','sm','md','lg'].map(function(size){
9472                     
9473                     if(typeof(config[size]) == 'undefined'){
9474                         return;
9475                     }
9476                      
9477                     if (!config[size]) { // 0 = hidden
9478                         // BS 4 '0' is treated as hide that column and below.
9479                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9480                         return;
9481                     }
9482                     
9483                     c.cls += ' col-' + size + '-' + config[size] + (
9484                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9485                     );
9486                     
9487                     
9488                 });
9489             }
9490             // at the end?
9491             
9492             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9493             
9494             
9495             
9496             
9497             header.cn.push(c)
9498         }
9499         
9500         return header;
9501     },
9502     
9503     renderBody : function()
9504     {
9505         var body = {
9506             tag: 'tbody',
9507             cn : [
9508                 {
9509                     tag: 'tr',
9510                     cn : [
9511                         {
9512                             tag : 'td',
9513                             colspan :  this.cm.getColumnCount()
9514                         }
9515                     ]
9516                 }
9517             ]
9518         };
9519         
9520         return body;
9521     },
9522     
9523     renderFooter : function()
9524     {
9525         var footer = {
9526             tag: 'tfoot',
9527             cn : [
9528                 {
9529                     tag: 'tr',
9530                     cn : [
9531                         {
9532                             tag : 'td',
9533                             colspan :  this.cm.getColumnCount()
9534                         }
9535                     ]
9536                 }
9537             ]
9538         };
9539         
9540         return footer;
9541     },
9542     
9543     
9544     
9545     onLoad : function()
9546     {
9547 //        Roo.log('ds onload');
9548         this.clear();
9549         
9550         var _this = this;
9551         var cm = this.cm;
9552         var ds = this.store;
9553         
9554         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9555             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9556             if (_this.store.sortInfo) {
9557                     
9558                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9559                     e.select('i', true).addClass(['fa-arrow-up']);
9560                 }
9561                 
9562                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9563                     e.select('i', true).addClass(['fa-arrow-down']);
9564                 }
9565             }
9566         });
9567         
9568         var tbody =  this.bodyEl;
9569               
9570         if(ds.getCount() > 0){
9571             ds.data.each(function(d,rowIndex){
9572                 var row =  this.renderRow(cm, ds, rowIndex);
9573                 
9574                 tbody.createChild(row);
9575                 
9576                 var _this = this;
9577                 
9578                 if(row.cellObjects.length){
9579                     Roo.each(row.cellObjects, function(r){
9580                         _this.renderCellObject(r);
9581                     })
9582                 }
9583                 
9584             }, this);
9585         }
9586         
9587         var tfoot = this.el.select('tfoot', true).first();
9588         
9589         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9590             
9591             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9592             
9593             var total = this.ds.getTotalCount();
9594             
9595             if(this.footer.pageSize < total){
9596                 this.mainFoot.show();
9597             }
9598         }
9599         
9600         Roo.each(this.el.select('tbody td', true).elements, function(e){
9601             e.on('mouseover', _this.onMouseover, _this);
9602         });
9603         
9604         Roo.each(this.el.select('tbody td', true).elements, function(e){
9605             e.on('mouseout', _this.onMouseout, _this);
9606         });
9607         this.fireEvent('rowsrendered', this);
9608         
9609         this.autoSize();
9610         
9611         this.initCSS(); /// resize cols
9612
9613         
9614     },
9615     
9616     
9617     onUpdate : function(ds,record)
9618     {
9619         this.refreshRow(record);
9620         this.autoSize();
9621     },
9622     
9623     onRemove : function(ds, record, index, isUpdate){
9624         if(isUpdate !== true){
9625             this.fireEvent("beforerowremoved", this, index, record);
9626         }
9627         var bt = this.bodyEl.dom;
9628         
9629         var rows = this.el.select('tbody > tr', true).elements;
9630         
9631         if(typeof(rows[index]) != 'undefined'){
9632             bt.removeChild(rows[index].dom);
9633         }
9634         
9635 //        if(bt.rows[index]){
9636 //            bt.removeChild(bt.rows[index]);
9637 //        }
9638         
9639         if(isUpdate !== true){
9640             //this.stripeRows(index);
9641             //this.syncRowHeights(index, index);
9642             //this.layout();
9643             this.fireEvent("rowremoved", this, index, record);
9644         }
9645     },
9646     
9647     onAdd : function(ds, records, rowIndex)
9648     {
9649         //Roo.log('on Add called');
9650         // - note this does not handle multiple adding very well..
9651         var bt = this.bodyEl.dom;
9652         for (var i =0 ; i < records.length;i++) {
9653             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9654             //Roo.log(records[i]);
9655             //Roo.log(this.store.getAt(rowIndex+i));
9656             this.insertRow(this.store, rowIndex + i, false);
9657             return;
9658         }
9659         
9660     },
9661     
9662     
9663     refreshRow : function(record){
9664         var ds = this.store, index;
9665         if(typeof record == 'number'){
9666             index = record;
9667             record = ds.getAt(index);
9668         }else{
9669             index = ds.indexOf(record);
9670             if (index < 0) {
9671                 return; // should not happen - but seems to 
9672             }
9673         }
9674         this.insertRow(ds, index, true);
9675         this.autoSize();
9676         this.onRemove(ds, record, index+1, true);
9677         this.autoSize();
9678         //this.syncRowHeights(index, index);
9679         //this.layout();
9680         this.fireEvent("rowupdated", this, index, record);
9681     },
9682     // private - called by RowSelection
9683     onRowSelect : function(rowIndex){
9684         var row = this.getRowDom(rowIndex);
9685         row.addClass(['bg-info','info']);
9686     },
9687     // private - called by RowSelection
9688     onRowDeselect : function(rowIndex)
9689     {
9690         if (rowIndex < 0) {
9691             return;
9692         }
9693         var row = this.getRowDom(rowIndex);
9694         row.removeClass(['bg-info','info']);
9695     },
9696       /**
9697      * Focuses the specified row.
9698      * @param {Number} row The row index
9699      */
9700     focusRow : function(row)
9701     {
9702         //Roo.log('GridView.focusRow');
9703         var x = this.bodyEl.dom.scrollLeft;
9704         this.focusCell(row, 0, false);
9705         this.bodyEl.dom.scrollLeft = x;
9706
9707     },
9708      /**
9709      * Focuses the specified cell.
9710      * @param {Number} row The row index
9711      * @param {Number} col The column index
9712      * @param {Boolean} hscroll false to disable horizontal scrolling
9713      */
9714     focusCell : function(row, col, hscroll)
9715     {
9716         //Roo.log('GridView.focusCell');
9717         var el = this.ensureVisible(row, col, hscroll);
9718         // not sure what focusEL achives = it's a <a> pos relative 
9719         //this.focusEl.alignTo(el, "tl-tl");
9720         //if(Roo.isGecko){
9721         //    this.focusEl.focus();
9722         //}else{
9723         //    this.focusEl.focus.defer(1, this.focusEl);
9724         //}
9725     },
9726     
9727      /**
9728      * Scrolls the specified cell into view
9729      * @param {Number} row The row index
9730      * @param {Number} col The column index
9731      * @param {Boolean} hscroll false to disable horizontal scrolling
9732      */
9733     ensureVisible : function(row, col, hscroll)
9734     {
9735         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9736         //return null; //disable for testing.
9737         if(typeof row != "number"){
9738             row = row.rowIndex;
9739         }
9740         if(row < 0 && row >= this.ds.getCount()){
9741             return  null;
9742         }
9743         col = (col !== undefined ? col : 0);
9744         var cm = this.cm;
9745         while(cm.isHidden(col)){
9746             col++;
9747         }
9748
9749         var el = this.getCellDom(row, col);
9750         if(!el){
9751             return null;
9752         }
9753         var c = this.bodyEl.dom;
9754
9755         var ctop = parseInt(el.offsetTop, 10);
9756         var cleft = parseInt(el.offsetLeft, 10);
9757         var cbot = ctop + el.offsetHeight;
9758         var cright = cleft + el.offsetWidth;
9759
9760         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9761         var ch = 0; //?? header is not withing the area?
9762         var stop = parseInt(c.scrollTop, 10);
9763         var sleft = parseInt(c.scrollLeft, 10);
9764         var sbot = stop + ch;
9765         var sright = sleft + c.clientWidth;
9766         /*
9767         Roo.log('GridView.ensureVisible:' +
9768                 ' ctop:' + ctop +
9769                 ' c.clientHeight:' + c.clientHeight +
9770                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9771                 ' stop:' + stop +
9772                 ' cbot:' + cbot +
9773                 ' sbot:' + sbot +
9774                 ' ch:' + ch  
9775                 );
9776         */
9777         if(ctop < stop){
9778             c.scrollTop = ctop;
9779             //Roo.log("set scrolltop to ctop DISABLE?");
9780         }else if(cbot > sbot){
9781             //Roo.log("set scrolltop to cbot-ch");
9782             c.scrollTop = cbot-ch;
9783         }
9784
9785         if(hscroll !== false){
9786             if(cleft < sleft){
9787                 c.scrollLeft = cleft;
9788             }else if(cright > sright){
9789                 c.scrollLeft = cright-c.clientWidth;
9790             }
9791         }
9792
9793         return el;
9794     },
9795     
9796     
9797     insertRow : function(dm, rowIndex, isUpdate){
9798         
9799         if(!isUpdate){
9800             this.fireEvent("beforerowsinserted", this, rowIndex);
9801         }
9802             //var s = this.getScrollState();
9803         var row = this.renderRow(this.cm, this.store, rowIndex);
9804         // insert before rowIndex..
9805         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9806         
9807         var _this = this;
9808                 
9809         if(row.cellObjects.length){
9810             Roo.each(row.cellObjects, function(r){
9811                 _this.renderCellObject(r);
9812             })
9813         }
9814             
9815         if(!isUpdate){
9816             this.fireEvent("rowsinserted", this, rowIndex);
9817             //this.syncRowHeights(firstRow, lastRow);
9818             //this.stripeRows(firstRow);
9819             //this.layout();
9820         }
9821         
9822     },
9823     
9824     
9825     getRowDom : function(rowIndex)
9826     {
9827         var rows = this.el.select('tbody > tr', true).elements;
9828         
9829         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9830         
9831     },
9832     getCellDom : function(rowIndex, colIndex)
9833     {
9834         var row = this.getRowDom(rowIndex);
9835         if (row === false) {
9836             return false;
9837         }
9838         var cols = row.select('td', true).elements;
9839         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9840         
9841     },
9842     
9843     // returns the object tree for a tr..
9844   
9845     
9846     renderRow : function(cm, ds, rowIndex) 
9847     {
9848         var d = ds.getAt(rowIndex);
9849         
9850         var row = {
9851             tag : 'tr',
9852             cls : 'x-row-' + rowIndex,
9853             cn : []
9854         };
9855             
9856         var cellObjects = [];
9857         
9858         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9859             var config = cm.config[i];
9860             
9861             var renderer = cm.getRenderer(i);
9862             var value = '';
9863             var id = false;
9864             
9865             if(typeof(renderer) !== 'undefined'){
9866                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9867             }
9868             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9869             // and are rendered into the cells after the row is rendered - using the id for the element.
9870             
9871             if(typeof(value) === 'object'){
9872                 id = Roo.id();
9873                 cellObjects.push({
9874                     container : id,
9875                     cfg : value 
9876                 })
9877             }
9878             
9879             var rowcfg = {
9880                 record: d,
9881                 rowIndex : rowIndex,
9882                 colIndex : i,
9883                 rowClass : ''
9884             };
9885
9886             this.fireEvent('rowclass', this, rowcfg);
9887             
9888             var td = {
9889                 tag: 'td',
9890                 // this might end up displaying HTML?
9891                 // this is too messy... - better to only do it on columsn you know are going to be too long
9892                 //tooltip : (typeof(value) === 'object') ? '' : value,
9893                 cls : rowcfg.rowClass + ' x-col-' + i,
9894                 style: '',
9895                 html: (typeof(value) === 'object') ? '' : value
9896             };
9897             
9898             if (id) {
9899                 td.id = id;
9900             }
9901             
9902             if(typeof(config.colspan) != 'undefined'){
9903                 td.colspan = config.colspan;
9904             }
9905             
9906             
9907             
9908             if(typeof(config.align) != 'undefined' && config.align.length){
9909                 td.style += ' text-align:' + config.align + ';';
9910             }
9911             if(typeof(config.valign) != 'undefined' && config.valign.length){
9912                 td.style += ' vertical-align:' + config.valign + ';';
9913             }
9914             /*
9915             if(typeof(config.width) != 'undefined'){
9916                 td.style += ' width:' +  config.width + 'px;';
9917             }
9918             */
9919             
9920             if(typeof(config.cursor) != 'undefined'){
9921                 td.style += ' cursor:' +  config.cursor + ';';
9922             }
9923             
9924             if(typeof(config.cls) != 'undefined'){
9925                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9926             }
9927             if (this.responsive) {
9928                 ['xs','sm','md','lg'].map(function(size){
9929                     
9930                     if(typeof(config[size]) == 'undefined'){
9931                         return;
9932                     }
9933                     
9934                     
9935                       
9936                     if (!config[size]) { // 0 = hidden
9937                         // BS 4 '0' is treated as hide that column and below.
9938                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9939                         return;
9940                     }
9941                     
9942                     td.cls += ' col-' + size + '-' + config[size] + (
9943                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9944                     );
9945                      
9946     
9947                 });
9948             }
9949             row.cn.push(td);
9950            
9951         }
9952         
9953         row.cellObjects = cellObjects;
9954         
9955         return row;
9956           
9957     },
9958     
9959     
9960     
9961     onBeforeLoad : function()
9962     {
9963         
9964     },
9965      /**
9966      * Remove all rows
9967      */
9968     clear : function()
9969     {
9970         this.el.select('tbody', true).first().dom.innerHTML = '';
9971     },
9972     /**
9973      * Show or hide a row.
9974      * @param {Number} rowIndex to show or hide
9975      * @param {Boolean} state hide
9976      */
9977     setRowVisibility : function(rowIndex, state)
9978     {
9979         var bt = this.bodyEl.dom;
9980         
9981         var rows = this.el.select('tbody > tr', true).elements;
9982         
9983         if(typeof(rows[rowIndex]) == 'undefined'){
9984             return;
9985         }
9986         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9987         
9988     },
9989     
9990     
9991     getSelectionModel : function(){
9992         if(!this.selModel){
9993             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9994         }
9995         return this.selModel;
9996     },
9997     /*
9998      * Render the Roo.bootstrap object from renderder
9999      */
10000     renderCellObject : function(r)
10001     {
10002         var _this = this;
10003         
10004         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10005         
10006         var t = r.cfg.render(r.container);
10007         
10008         if(r.cfg.cn){
10009             Roo.each(r.cfg.cn, function(c){
10010                 var child = {
10011                     container: t.getChildContainer(),
10012                     cfg: c
10013                 };
10014                 _this.renderCellObject(child);
10015             })
10016         }
10017     },
10018     /**
10019      * get the Row Index from a dom element.
10020      * @param {Roo.Element} row The row to look for
10021      * @returns {Number} the row
10022      */
10023     getRowIndex : function(row)
10024     {
10025         var rowIndex = -1;
10026         
10027         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10028             if(el != row){
10029                 return;
10030             }
10031             
10032             rowIndex = index;
10033         });
10034         
10035         return rowIndex;
10036     },
10037     /**
10038      * get the header TH element for columnIndex
10039      * @param {Number} columnIndex
10040      * @returns {Roo.Element}
10041      */
10042     getHeaderIndex: function(colIndex)
10043     {
10044         var cols = this.headEl.select('th', true).elements;
10045         return cols[colIndex]; 
10046     },
10047     /**
10048      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10049      * @param {domElement} cell to look for
10050      * @returns {Number} the column
10051      */
10052     getCellIndex : function(cell)
10053     {
10054         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10055         if(id){
10056             return parseInt(id[1], 10);
10057         }
10058         return 0;
10059     },
10060      /**
10061      * Returns the grid's underlying element = used by panel.Grid
10062      * @return {Element} The element
10063      */
10064     getGridEl : function(){
10065         return this.el;
10066     },
10067      /**
10068      * Forces a resize - used by panel.Grid
10069      * @return {Element} The element
10070      */
10071     autoSize : function()
10072     {
10073         //var ctr = Roo.get(this.container.dom.parentElement);
10074         var ctr = Roo.get(this.el.dom);
10075         
10076         var thd = this.getGridEl().select('thead',true).first();
10077         var tbd = this.getGridEl().select('tbody', true).first();
10078         var tfd = this.getGridEl().select('tfoot', true).first();
10079         
10080         var cw = ctr.getWidth();
10081         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10082         
10083         if (tbd) {
10084             
10085             tbd.setWidth(ctr.getWidth());
10086             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10087             // this needs fixing for various usage - currently only hydra job advers I think..
10088             //tdb.setHeight(
10089             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10090             //); 
10091             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10092             cw -= barsize;
10093         }
10094         cw = Math.max(cw, this.totalWidth);
10095         this.getGridEl().select('tbody tr',true).setWidth(cw);
10096         this.initCSS();
10097         
10098         // resize 'expandable coloumn?
10099         
10100         return; // we doe not have a view in this design..
10101         
10102     },
10103     onBodyScroll: function()
10104     {
10105         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10106         if(this.headEl){
10107             this.headEl.setStyle({
10108                 'position' : 'relative',
10109                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10110             });
10111         }
10112         
10113         if(this.lazyLoad){
10114             
10115             var scrollHeight = this.bodyEl.dom.scrollHeight;
10116             
10117             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10118             
10119             var height = this.bodyEl.getHeight();
10120             
10121             if(scrollHeight - height == scrollTop) {
10122                 
10123                 var total = this.ds.getTotalCount();
10124                 
10125                 if(this.footer.cursor + this.footer.pageSize < total){
10126                     
10127                     this.footer.ds.load({
10128                         params : {
10129                             start : this.footer.cursor + this.footer.pageSize,
10130                             limit : this.footer.pageSize
10131                         },
10132                         add : true
10133                     });
10134                 }
10135             }
10136             
10137         }
10138     },
10139     onColumnSplitterMoved : function(i, diff)
10140     {
10141         this.userResized = true;
10142         
10143         var cm = this.colModel;
10144         
10145         var w = this.getHeaderIndex(i).getWidth() + diff;
10146         
10147         
10148         cm.setColumnWidth(i, w, true);
10149         this.initCSS();
10150         //var cid = cm.getColumnId(i); << not used in this version?
10151        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10152         
10153         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10154         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10155         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10156 */
10157         //this.updateSplitters();
10158         //this.layout(); << ??
10159         this.fireEvent("columnresize", i, w);
10160     },
10161     onHeaderChange : function()
10162     {
10163         var header = this.renderHeader();
10164         var table = this.el.select('table', true).first();
10165         
10166         this.headEl.remove();
10167         this.headEl = table.createChild(header, this.bodyEl, false);
10168         
10169         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10170             e.on('click', this.sort, this);
10171         }, this);
10172         
10173         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10174             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10175         }
10176         
10177     },
10178     
10179     onHiddenChange : function(colModel, colIndex, hidden)
10180     {
10181         /*
10182         this.cm.setHidden()
10183         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10184         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10185         
10186         this.CSS.updateRule(thSelector, "display", "");
10187         this.CSS.updateRule(tdSelector, "display", "");
10188         
10189         if(hidden){
10190             this.CSS.updateRule(thSelector, "display", "none");
10191             this.CSS.updateRule(tdSelector, "display", "none");
10192         }
10193         */
10194         // onload calls initCSS()
10195         this.onHeaderChange();
10196         this.onLoad();
10197     },
10198     
10199     setColumnWidth: function(col_index, width)
10200     {
10201         // width = "md-2 xs-2..."
10202         if(!this.colModel.config[col_index]) {
10203             return;
10204         }
10205         
10206         var w = width.split(" ");
10207         
10208         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10209         
10210         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10211         
10212         
10213         for(var j = 0; j < w.length; j++) {
10214             
10215             if(!w[j]) {
10216                 continue;
10217             }
10218             
10219             var size_cls = w[j].split("-");
10220             
10221             if(!Number.isInteger(size_cls[1] * 1)) {
10222                 continue;
10223             }
10224             
10225             if(!this.colModel.config[col_index][size_cls[0]]) {
10226                 continue;
10227             }
10228             
10229             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10230                 continue;
10231             }
10232             
10233             h_row[0].classList.replace(
10234                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10235                 "col-"+size_cls[0]+"-"+size_cls[1]
10236             );
10237             
10238             for(var i = 0; i < rows.length; i++) {
10239                 
10240                 var size_cls = w[j].split("-");
10241                 
10242                 if(!Number.isInteger(size_cls[1] * 1)) {
10243                     continue;
10244                 }
10245                 
10246                 if(!this.colModel.config[col_index][size_cls[0]]) {
10247                     continue;
10248                 }
10249                 
10250                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10251                     continue;
10252                 }
10253                 
10254                 rows[i].classList.replace(
10255                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10256                     "col-"+size_cls[0]+"-"+size_cls[1]
10257                 );
10258             }
10259             
10260             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10261         }
10262     }
10263 });
10264
10265 // currently only used to find the split on drag.. 
10266 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10267
10268 /**
10269  * @depricated
10270 */
10271 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10272 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10273 /*
10274  * - LGPL
10275  *
10276  * table cell
10277  * 
10278  */
10279
10280 /**
10281  * @class Roo.bootstrap.TableCell
10282  * @extends Roo.bootstrap.Component
10283  * Bootstrap TableCell class
10284  * @cfg {String} html cell contain text
10285  * @cfg {String} cls cell class
10286  * @cfg {String} tag cell tag (td|th) default td
10287  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10288  * @cfg {String} align Aligns the content in a cell
10289  * @cfg {String} axis Categorizes cells
10290  * @cfg {String} bgcolor Specifies the background color of a cell
10291  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10292  * @cfg {Number} colspan Specifies the number of columns a cell should span
10293  * @cfg {String} headers Specifies one or more header cells a cell is related to
10294  * @cfg {Number} height Sets the height of a cell
10295  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10296  * @cfg {Number} rowspan Sets the number of rows a cell should span
10297  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10298  * @cfg {String} valign Vertical aligns the content in a cell
10299  * @cfg {Number} width Specifies the width of a cell
10300  * 
10301  * @constructor
10302  * Create a new TableCell
10303  * @param {Object} config The config object
10304  */
10305
10306 Roo.bootstrap.TableCell = function(config){
10307     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10308 };
10309
10310 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10311     
10312     html: false,
10313     cls: false,
10314     tag: false,
10315     abbr: false,
10316     align: false,
10317     axis: false,
10318     bgcolor: false,
10319     charoff: false,
10320     colspan: false,
10321     headers: false,
10322     height: false,
10323     nowrap: false,
10324     rowspan: false,
10325     scope: false,
10326     valign: false,
10327     width: false,
10328     
10329     
10330     getAutoCreate : function(){
10331         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10332         
10333         cfg = {
10334             tag: 'td'
10335         };
10336         
10337         if(this.tag){
10338             cfg.tag = this.tag;
10339         }
10340         
10341         if (this.html) {
10342             cfg.html=this.html
10343         }
10344         if (this.cls) {
10345             cfg.cls=this.cls
10346         }
10347         if (this.abbr) {
10348             cfg.abbr=this.abbr
10349         }
10350         if (this.align) {
10351             cfg.align=this.align
10352         }
10353         if (this.axis) {
10354             cfg.axis=this.axis
10355         }
10356         if (this.bgcolor) {
10357             cfg.bgcolor=this.bgcolor
10358         }
10359         if (this.charoff) {
10360             cfg.charoff=this.charoff
10361         }
10362         if (this.colspan) {
10363             cfg.colspan=this.colspan
10364         }
10365         if (this.headers) {
10366             cfg.headers=this.headers
10367         }
10368         if (this.height) {
10369             cfg.height=this.height
10370         }
10371         if (this.nowrap) {
10372             cfg.nowrap=this.nowrap
10373         }
10374         if (this.rowspan) {
10375             cfg.rowspan=this.rowspan
10376         }
10377         if (this.scope) {
10378             cfg.scope=this.scope
10379         }
10380         if (this.valign) {
10381             cfg.valign=this.valign
10382         }
10383         if (this.width) {
10384             cfg.width=this.width
10385         }
10386         
10387         
10388         return cfg;
10389     }
10390    
10391 });
10392
10393  
10394
10395  /*
10396  * - LGPL
10397  *
10398  * table row
10399  * 
10400  */
10401
10402 /**
10403  * @class Roo.bootstrap.TableRow
10404  * @extends Roo.bootstrap.Component
10405  * Bootstrap TableRow class
10406  * @cfg {String} cls row class
10407  * @cfg {String} align Aligns the content in a table row
10408  * @cfg {String} bgcolor Specifies a background color for a table row
10409  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10410  * @cfg {String} valign Vertical aligns the content in a table row
10411  * 
10412  * @constructor
10413  * Create a new TableRow
10414  * @param {Object} config The config object
10415  */
10416
10417 Roo.bootstrap.TableRow = function(config){
10418     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10419 };
10420
10421 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10422     
10423     cls: false,
10424     align: false,
10425     bgcolor: false,
10426     charoff: false,
10427     valign: false,
10428     
10429     getAutoCreate : function(){
10430         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10431         
10432         cfg = {
10433             tag: 'tr'
10434         };
10435             
10436         if(this.cls){
10437             cfg.cls = this.cls;
10438         }
10439         if(this.align){
10440             cfg.align = this.align;
10441         }
10442         if(this.bgcolor){
10443             cfg.bgcolor = this.bgcolor;
10444         }
10445         if(this.charoff){
10446             cfg.charoff = this.charoff;
10447         }
10448         if(this.valign){
10449             cfg.valign = this.valign;
10450         }
10451         
10452         return cfg;
10453     }
10454    
10455 });
10456
10457  
10458
10459  /*
10460  * - LGPL
10461  *
10462  * table body
10463  * 
10464  */
10465
10466 /**
10467  * @class Roo.bootstrap.TableBody
10468  * @extends Roo.bootstrap.Component
10469  * Bootstrap TableBody class
10470  * @cfg {String} cls element class
10471  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10472  * @cfg {String} align Aligns the content inside the element
10473  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10474  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10475  * 
10476  * @constructor
10477  * Create a new TableBody
10478  * @param {Object} config The config object
10479  */
10480
10481 Roo.bootstrap.TableBody = function(config){
10482     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10483 };
10484
10485 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10486     
10487     cls: false,
10488     tag: false,
10489     align: false,
10490     charoff: false,
10491     valign: false,
10492     
10493     getAutoCreate : function(){
10494         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10495         
10496         cfg = {
10497             tag: 'tbody'
10498         };
10499             
10500         if (this.cls) {
10501             cfg.cls=this.cls
10502         }
10503         if(this.tag){
10504             cfg.tag = this.tag;
10505         }
10506         
10507         if(this.align){
10508             cfg.align = this.align;
10509         }
10510         if(this.charoff){
10511             cfg.charoff = this.charoff;
10512         }
10513         if(this.valign){
10514             cfg.valign = this.valign;
10515         }
10516         
10517         return cfg;
10518     }
10519     
10520     
10521 //    initEvents : function()
10522 //    {
10523 //        
10524 //        if(!this.store){
10525 //            return;
10526 //        }
10527 //        
10528 //        this.store = Roo.factory(this.store, Roo.data);
10529 //        this.store.on('load', this.onLoad, this);
10530 //        
10531 //        this.store.load();
10532 //        
10533 //    },
10534 //    
10535 //    onLoad: function () 
10536 //    {   
10537 //        this.fireEvent('load', this);
10538 //    }
10539 //    
10540 //   
10541 });
10542
10543  
10544
10545  /*
10546  * Based on:
10547  * Ext JS Library 1.1.1
10548  * Copyright(c) 2006-2007, Ext JS, LLC.
10549  *
10550  * Originally Released Under LGPL - original licence link has changed is not relivant.
10551  *
10552  * Fork - LGPL
10553  * <script type="text/javascript">
10554  */
10555
10556 // as we use this in bootstrap.
10557 Roo.namespace('Roo.form');
10558  /**
10559  * @class Roo.form.Action
10560  * Internal Class used to handle form actions
10561  * @constructor
10562  * @param {Roo.form.BasicForm} el The form element or its id
10563  * @param {Object} config Configuration options
10564  */
10565
10566  
10567  
10568 // define the action interface
10569 Roo.form.Action = function(form, options){
10570     this.form = form;
10571     this.options = options || {};
10572 };
10573 /**
10574  * Client Validation Failed
10575  * @const 
10576  */
10577 Roo.form.Action.CLIENT_INVALID = 'client';
10578 /**
10579  * Server Validation Failed
10580  * @const 
10581  */
10582 Roo.form.Action.SERVER_INVALID = 'server';
10583  /**
10584  * Connect to Server Failed
10585  * @const 
10586  */
10587 Roo.form.Action.CONNECT_FAILURE = 'connect';
10588 /**
10589  * Reading Data from Server Failed
10590  * @const 
10591  */
10592 Roo.form.Action.LOAD_FAILURE = 'load';
10593
10594 Roo.form.Action.prototype = {
10595     type : 'default',
10596     failureType : undefined,
10597     response : undefined,
10598     result : undefined,
10599
10600     // interface method
10601     run : function(options){
10602
10603     },
10604
10605     // interface method
10606     success : function(response){
10607
10608     },
10609
10610     // interface method
10611     handleResponse : function(response){
10612
10613     },
10614
10615     // default connection failure
10616     failure : function(response){
10617         
10618         this.response = response;
10619         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10620         this.form.afterAction(this, false);
10621     },
10622
10623     processResponse : function(response){
10624         this.response = response;
10625         if(!response.responseText){
10626             return true;
10627         }
10628         this.result = this.handleResponse(response);
10629         return this.result;
10630     },
10631
10632     // utility functions used internally
10633     getUrl : function(appendParams){
10634         var url = this.options.url || this.form.url || this.form.el.dom.action;
10635         if(appendParams){
10636             var p = this.getParams();
10637             if(p){
10638                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10639             }
10640         }
10641         return url;
10642     },
10643
10644     getMethod : function(){
10645         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10646     },
10647
10648     getParams : function(){
10649         var bp = this.form.baseParams;
10650         var p = this.options.params;
10651         if(p){
10652             if(typeof p == "object"){
10653                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10654             }else if(typeof p == 'string' && bp){
10655                 p += '&' + Roo.urlEncode(bp);
10656             }
10657         }else if(bp){
10658             p = Roo.urlEncode(bp);
10659         }
10660         return p;
10661     },
10662
10663     createCallback : function(){
10664         return {
10665             success: this.success,
10666             failure: this.failure,
10667             scope: this,
10668             timeout: (this.form.timeout*1000),
10669             upload: this.form.fileUpload ? this.success : undefined
10670         };
10671     }
10672 };
10673
10674 Roo.form.Action.Submit = function(form, options){
10675     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10676 };
10677
10678 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10679     type : 'submit',
10680
10681     haveProgress : false,
10682     uploadComplete : false,
10683     
10684     // uploadProgress indicator.
10685     uploadProgress : function()
10686     {
10687         if (!this.form.progressUrl) {
10688             return;
10689         }
10690         
10691         if (!this.haveProgress) {
10692             Roo.MessageBox.progress("Uploading", "Uploading");
10693         }
10694         if (this.uploadComplete) {
10695            Roo.MessageBox.hide();
10696            return;
10697         }
10698         
10699         this.haveProgress = true;
10700    
10701         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10702         
10703         var c = new Roo.data.Connection();
10704         c.request({
10705             url : this.form.progressUrl,
10706             params: {
10707                 id : uid
10708             },
10709             method: 'GET',
10710             success : function(req){
10711                //console.log(data);
10712                 var rdata = false;
10713                 var edata;
10714                 try  {
10715                    rdata = Roo.decode(req.responseText)
10716                 } catch (e) {
10717                     Roo.log("Invalid data from server..");
10718                     Roo.log(edata);
10719                     return;
10720                 }
10721                 if (!rdata || !rdata.success) {
10722                     Roo.log(rdata);
10723                     Roo.MessageBox.alert(Roo.encode(rdata));
10724                     return;
10725                 }
10726                 var data = rdata.data;
10727                 
10728                 if (this.uploadComplete) {
10729                    Roo.MessageBox.hide();
10730                    return;
10731                 }
10732                    
10733                 if (data){
10734                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10735                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10736                     );
10737                 }
10738                 this.uploadProgress.defer(2000,this);
10739             },
10740        
10741             failure: function(data) {
10742                 Roo.log('progress url failed ');
10743                 Roo.log(data);
10744             },
10745             scope : this
10746         });
10747            
10748     },
10749     
10750     
10751     run : function()
10752     {
10753         // run get Values on the form, so it syncs any secondary forms.
10754         this.form.getValues();
10755         
10756         var o = this.options;
10757         var method = this.getMethod();
10758         var isPost = method == 'POST';
10759         if(o.clientValidation === false || this.form.isValid()){
10760             
10761             if (this.form.progressUrl) {
10762                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10763                     (new Date() * 1) + '' + Math.random());
10764                     
10765             } 
10766             
10767             
10768             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10769                 form:this.form.el.dom,
10770                 url:this.getUrl(!isPost),
10771                 method: method,
10772                 params:isPost ? this.getParams() : null,
10773                 isUpload: this.form.fileUpload,
10774                 formData : this.form.formData
10775             }));
10776             
10777             this.uploadProgress();
10778
10779         }else if (o.clientValidation !== false){ // client validation failed
10780             this.failureType = Roo.form.Action.CLIENT_INVALID;
10781             this.form.afterAction(this, false);
10782         }
10783     },
10784
10785     success : function(response)
10786     {
10787         this.uploadComplete= true;
10788         if (this.haveProgress) {
10789             Roo.MessageBox.hide();
10790         }
10791         
10792         
10793         var result = this.processResponse(response);
10794         if(result === true || result.success){
10795             this.form.afterAction(this, true);
10796             return;
10797         }
10798         if(result.errors){
10799             this.form.markInvalid(result.errors);
10800             this.failureType = Roo.form.Action.SERVER_INVALID;
10801         }
10802         this.form.afterAction(this, false);
10803     },
10804     failure : function(response)
10805     {
10806         this.uploadComplete= true;
10807         if (this.haveProgress) {
10808             Roo.MessageBox.hide();
10809         }
10810         
10811         this.response = response;
10812         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10813         this.form.afterAction(this, false);
10814     },
10815     
10816     handleResponse : function(response){
10817         if(this.form.errorReader){
10818             var rs = this.form.errorReader.read(response);
10819             var errors = [];
10820             if(rs.records){
10821                 for(var i = 0, len = rs.records.length; i < len; i++) {
10822                     var r = rs.records[i];
10823                     errors[i] = r.data;
10824                 }
10825             }
10826             if(errors.length < 1){
10827                 errors = null;
10828             }
10829             return {
10830                 success : rs.success,
10831                 errors : errors
10832             };
10833         }
10834         var ret = false;
10835         try {
10836             ret = Roo.decode(response.responseText);
10837         } catch (e) {
10838             ret = {
10839                 success: false,
10840                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10841                 errors : []
10842             };
10843         }
10844         return ret;
10845         
10846     }
10847 });
10848
10849
10850 Roo.form.Action.Load = function(form, options){
10851     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10852     this.reader = this.form.reader;
10853 };
10854
10855 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10856     type : 'load',
10857
10858     run : function(){
10859         
10860         Roo.Ajax.request(Roo.apply(
10861                 this.createCallback(), {
10862                     method:this.getMethod(),
10863                     url:this.getUrl(false),
10864                     params:this.getParams()
10865         }));
10866     },
10867
10868     success : function(response){
10869         
10870         var result = this.processResponse(response);
10871         if(result === true || !result.success || !result.data){
10872             this.failureType = Roo.form.Action.LOAD_FAILURE;
10873             this.form.afterAction(this, false);
10874             return;
10875         }
10876         this.form.clearInvalid();
10877         this.form.setValues(result.data);
10878         this.form.afterAction(this, true);
10879     },
10880
10881     handleResponse : function(response){
10882         if(this.form.reader){
10883             var rs = this.form.reader.read(response);
10884             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10885             return {
10886                 success : rs.success,
10887                 data : data
10888             };
10889         }
10890         return Roo.decode(response.responseText);
10891     }
10892 });
10893
10894 Roo.form.Action.ACTION_TYPES = {
10895     'load' : Roo.form.Action.Load,
10896     'submit' : Roo.form.Action.Submit
10897 };/*
10898  * - LGPL
10899  *
10900  * form
10901  *
10902  */
10903
10904 /**
10905  * @class Roo.bootstrap.Form
10906  * @extends Roo.bootstrap.Component
10907  * Bootstrap Form class
10908  * @cfg {String} method  GET | POST (default POST)
10909  * @cfg {String} labelAlign top | left (default top)
10910  * @cfg {String} align left  | right - for navbars
10911  * @cfg {Boolean} loadMask load mask when submit (default true)
10912
10913  *
10914  * @constructor
10915  * Create a new Form
10916  * @param {Object} config The config object
10917  */
10918
10919
10920 Roo.bootstrap.Form = function(config){
10921     
10922     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10923     
10924     Roo.bootstrap.Form.popover.apply();
10925     
10926     this.addEvents({
10927         /**
10928          * @event clientvalidation
10929          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10930          * @param {Form} this
10931          * @param {Boolean} valid true if the form has passed client-side validation
10932          */
10933         clientvalidation: true,
10934         /**
10935          * @event beforeaction
10936          * Fires before any action is performed. Return false to cancel the action.
10937          * @param {Form} this
10938          * @param {Action} action The action to be performed
10939          */
10940         beforeaction: true,
10941         /**
10942          * @event actionfailed
10943          * Fires when an action fails.
10944          * @param {Form} this
10945          * @param {Action} action The action that failed
10946          */
10947         actionfailed : true,
10948         /**
10949          * @event actioncomplete
10950          * Fires when an action is completed.
10951          * @param {Form} this
10952          * @param {Action} action The action that completed
10953          */
10954         actioncomplete : true
10955     });
10956 };
10957
10958 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10959
10960      /**
10961      * @cfg {String} method
10962      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10963      */
10964     method : 'POST',
10965     /**
10966      * @cfg {String} url
10967      * The URL to use for form actions if one isn't supplied in the action options.
10968      */
10969     /**
10970      * @cfg {Boolean} fileUpload
10971      * Set to true if this form is a file upload.
10972      */
10973
10974     /**
10975      * @cfg {Object} baseParams
10976      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10977      */
10978
10979     /**
10980      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10981      */
10982     timeout: 30,
10983     /**
10984      * @cfg {Sting} align (left|right) for navbar forms
10985      */
10986     align : 'left',
10987
10988     // private
10989     activeAction : null,
10990
10991     /**
10992      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10993      * element by passing it or its id or mask the form itself by passing in true.
10994      * @type Mixed
10995      */
10996     waitMsgTarget : false,
10997
10998     loadMask : true,
10999     
11000     /**
11001      * @cfg {Boolean} errorMask (true|false) default false
11002      */
11003     errorMask : false,
11004     
11005     /**
11006      * @cfg {Number} maskOffset Default 100
11007      */
11008     maskOffset : 100,
11009     
11010     /**
11011      * @cfg {Boolean} maskBody
11012      */
11013     maskBody : false,
11014
11015     getAutoCreate : function(){
11016
11017         var cfg = {
11018             tag: 'form',
11019             method : this.method || 'POST',
11020             id : this.id || Roo.id(),
11021             cls : ''
11022         };
11023         if (this.parent().xtype.match(/^Nav/)) {
11024             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11025
11026         }
11027
11028         if (this.labelAlign == 'left' ) {
11029             cfg.cls += ' form-horizontal';
11030         }
11031
11032
11033         return cfg;
11034     },
11035     initEvents : function()
11036     {
11037         this.el.on('submit', this.onSubmit, this);
11038         // this was added as random key presses on the form where triggering form submit.
11039         this.el.on('keypress', function(e) {
11040             if (e.getCharCode() != 13) {
11041                 return true;
11042             }
11043             // we might need to allow it for textareas.. and some other items.
11044             // check e.getTarget().
11045
11046             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11047                 return true;
11048             }
11049
11050             Roo.log("keypress blocked");
11051
11052             e.preventDefault();
11053             return false;
11054         });
11055         
11056     },
11057     // private
11058     onSubmit : function(e){
11059         e.stopEvent();
11060     },
11061
11062      /**
11063      * Returns true if client-side validation on the form is successful.
11064      * @return Boolean
11065      */
11066     isValid : function(){
11067         var items = this.getItems();
11068         var valid = true;
11069         var target = false;
11070         
11071         items.each(function(f){
11072             
11073             if(f.validate()){
11074                 return;
11075             }
11076             
11077             Roo.log('invalid field: ' + f.name);
11078             
11079             valid = false;
11080
11081             if(!target && f.el.isVisible(true)){
11082                 target = f;
11083             }
11084            
11085         });
11086         
11087         if(this.errorMask && !valid){
11088             Roo.bootstrap.Form.popover.mask(this, target);
11089         }
11090         
11091         return valid;
11092     },
11093     
11094     /**
11095      * Returns true if any fields in this form have changed since their original load.
11096      * @return Boolean
11097      */
11098     isDirty : function(){
11099         var dirty = false;
11100         var items = this.getItems();
11101         items.each(function(f){
11102            if(f.isDirty()){
11103                dirty = true;
11104                return false;
11105            }
11106            return true;
11107         });
11108         return dirty;
11109     },
11110      /**
11111      * Performs a predefined action (submit or load) or custom actions you define on this form.
11112      * @param {String} actionName The name of the action type
11113      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11114      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11115      * accept other config options):
11116      * <pre>
11117 Property          Type             Description
11118 ----------------  ---------------  ----------------------------------------------------------------------------------
11119 url               String           The url for the action (defaults to the form's url)
11120 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11121 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11122 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11123                                    validate the form on the client (defaults to false)
11124      * </pre>
11125      * @return {BasicForm} this
11126      */
11127     doAction : function(action, options){
11128         if(typeof action == 'string'){
11129             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11130         }
11131         if(this.fireEvent('beforeaction', this, action) !== false){
11132             this.beforeAction(action);
11133             action.run.defer(100, action);
11134         }
11135         return this;
11136     },
11137
11138     // private
11139     beforeAction : function(action){
11140         var o = action.options;
11141         
11142         if(this.loadMask){
11143             
11144             if(this.maskBody){
11145                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11146             } else {
11147                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11148             }
11149         }
11150         // not really supported yet.. ??
11151
11152         //if(this.waitMsgTarget === true){
11153         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154         //}else if(this.waitMsgTarget){
11155         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11156         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11157         //}else {
11158         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11159        // }
11160
11161     },
11162
11163     // private
11164     afterAction : function(action, success){
11165         this.activeAction = null;
11166         var o = action.options;
11167
11168         if(this.loadMask){
11169             
11170             if(this.maskBody){
11171                 Roo.get(document.body).unmask();
11172             } else {
11173                 this.el.unmask();
11174             }
11175         }
11176         
11177         //if(this.waitMsgTarget === true){
11178 //            this.el.unmask();
11179         //}else if(this.waitMsgTarget){
11180         //    this.waitMsgTarget.unmask();
11181         //}else{
11182         //    Roo.MessageBox.updateProgress(1);
11183         //    Roo.MessageBox.hide();
11184        // }
11185         //
11186         if(success){
11187             if(o.reset){
11188                 this.reset();
11189             }
11190             Roo.callback(o.success, o.scope, [this, action]);
11191             this.fireEvent('actioncomplete', this, action);
11192
11193         }else{
11194
11195             // failure condition..
11196             // we have a scenario where updates need confirming.
11197             // eg. if a locking scenario exists..
11198             // we look for { errors : { needs_confirm : true }} in the response.
11199             if (
11200                 (typeof(action.result) != 'undefined')  &&
11201                 (typeof(action.result.errors) != 'undefined')  &&
11202                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11203            ){
11204                 var _t = this;
11205                 Roo.log("not supported yet");
11206                  /*
11207
11208                 Roo.MessageBox.confirm(
11209                     "Change requires confirmation",
11210                     action.result.errorMsg,
11211                     function(r) {
11212                         if (r != 'yes') {
11213                             return;
11214                         }
11215                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11216                     }
11217
11218                 );
11219                 */
11220
11221
11222                 return;
11223             }
11224
11225             Roo.callback(o.failure, o.scope, [this, action]);
11226             // show an error message if no failed handler is set..
11227             if (!this.hasListener('actionfailed')) {
11228                 Roo.log("need to add dialog support");
11229                 /*
11230                 Roo.MessageBox.alert("Error",
11231                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11232                         action.result.errorMsg :
11233                         "Saving Failed, please check your entries or try again"
11234                 );
11235                 */
11236             }
11237
11238             this.fireEvent('actionfailed', this, action);
11239         }
11240
11241     },
11242     /**
11243      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11244      * @param {String} id The value to search for
11245      * @return Field
11246      */
11247     findField : function(id){
11248         var items = this.getItems();
11249         var field = items.get(id);
11250         if(!field){
11251              items.each(function(f){
11252                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11253                     field = f;
11254                     return false;
11255                 }
11256                 return true;
11257             });
11258         }
11259         return field || null;
11260     },
11261      /**
11262      * Mark fields in this form invalid in bulk.
11263      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11264      * @return {BasicForm} this
11265      */
11266     markInvalid : function(errors){
11267         if(errors instanceof Array){
11268             for(var i = 0, len = errors.length; i < len; i++){
11269                 var fieldError = errors[i];
11270                 var f = this.findField(fieldError.id);
11271                 if(f){
11272                     f.markInvalid(fieldError.msg);
11273                 }
11274             }
11275         }else{
11276             var field, id;
11277             for(id in errors){
11278                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11279                     field.markInvalid(errors[id]);
11280                 }
11281             }
11282         }
11283         //Roo.each(this.childForms || [], function (f) {
11284         //    f.markInvalid(errors);
11285         //});
11286
11287         return this;
11288     },
11289
11290     /**
11291      * Set values for fields in this form in bulk.
11292      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11293      * @return {BasicForm} this
11294      */
11295     setValues : function(values){
11296         if(values instanceof Array){ // array of objects
11297             for(var i = 0, len = values.length; i < len; i++){
11298                 var v = values[i];
11299                 var f = this.findField(v.id);
11300                 if(f){
11301                     f.setValue(v.value);
11302                     if(this.trackResetOnLoad){
11303                         f.originalValue = f.getValue();
11304                     }
11305                 }
11306             }
11307         }else{ // object hash
11308             var field, id;
11309             for(id in values){
11310                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11311
11312                     if (field.setFromData &&
11313                         field.valueField &&
11314                         field.displayField &&
11315                         // combos' with local stores can
11316                         // be queried via setValue()
11317                         // to set their value..
11318                         (field.store && !field.store.isLocal)
11319                         ) {
11320                         // it's a combo
11321                         var sd = { };
11322                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11323                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11324                         field.setFromData(sd);
11325
11326                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11327                         
11328                         field.setFromData(values);
11329                         
11330                     } else {
11331                         field.setValue(values[id]);
11332                     }
11333
11334
11335                     if(this.trackResetOnLoad){
11336                         field.originalValue = field.getValue();
11337                     }
11338                 }
11339             }
11340         }
11341
11342         //Roo.each(this.childForms || [], function (f) {
11343         //    f.setValues(values);
11344         //});
11345
11346         return this;
11347     },
11348
11349     /**
11350      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11351      * they are returned as an array.
11352      * @param {Boolean} asString
11353      * @return {Object}
11354      */
11355     getValues : function(asString){
11356         //if (this.childForms) {
11357             // copy values from the child forms
11358         //    Roo.each(this.childForms, function (f) {
11359         //        this.setValues(f.getValues());
11360         //    }, this);
11361         //}
11362
11363
11364
11365         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11366         if(asString === true){
11367             return fs;
11368         }
11369         return Roo.urlDecode(fs);
11370     },
11371
11372     /**
11373      * Returns the fields in this form as an object with key/value pairs.
11374      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11375      * @return {Object}
11376      */
11377     getFieldValues : function(with_hidden)
11378     {
11379         var items = this.getItems();
11380         var ret = {};
11381         items.each(function(f){
11382             
11383             if (!f.getName()) {
11384                 return;
11385             }
11386             
11387             var v = f.getValue();
11388             
11389             if (f.inputType =='radio') {
11390                 if (typeof(ret[f.getName()]) == 'undefined') {
11391                     ret[f.getName()] = ''; // empty..
11392                 }
11393
11394                 if (!f.el.dom.checked) {
11395                     return;
11396
11397                 }
11398                 v = f.el.dom.value;
11399
11400             }
11401             
11402             if(f.xtype == 'MoneyField'){
11403                 ret[f.currencyName] = f.getCurrency();
11404             }
11405
11406             // not sure if this supported any more..
11407             if ((typeof(v) == 'object') && f.getRawValue) {
11408                 v = f.getRawValue() ; // dates..
11409             }
11410             // combo boxes where name != hiddenName...
11411             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11412                 ret[f.name] = f.getRawValue();
11413             }
11414             ret[f.getName()] = v;
11415         });
11416
11417         return ret;
11418     },
11419
11420     /**
11421      * Clears all invalid messages in this form.
11422      * @return {BasicForm} this
11423      */
11424     clearInvalid : function(){
11425         var items = this.getItems();
11426
11427         items.each(function(f){
11428            f.clearInvalid();
11429         });
11430
11431         return this;
11432     },
11433
11434     /**
11435      * Resets this form.
11436      * @return {BasicForm} this
11437      */
11438     reset : function(){
11439         var items = this.getItems();
11440         items.each(function(f){
11441             f.reset();
11442         });
11443
11444         Roo.each(this.childForms || [], function (f) {
11445             f.reset();
11446         });
11447
11448
11449         return this;
11450     },
11451     
11452     getItems : function()
11453     {
11454         var r=new Roo.util.MixedCollection(false, function(o){
11455             return o.id || (o.id = Roo.id());
11456         });
11457         var iter = function(el) {
11458             if (el.inputEl) {
11459                 r.add(el);
11460             }
11461             if (!el.items) {
11462                 return;
11463             }
11464             Roo.each(el.items,function(e) {
11465                 iter(e);
11466             });
11467         };
11468
11469         iter(this);
11470         return r;
11471     },
11472     
11473     hideFields : function(items)
11474     {
11475         Roo.each(items, function(i){
11476             
11477             var f = this.findField(i);
11478             
11479             if(!f){
11480                 return;
11481             }
11482             
11483             f.hide();
11484             
11485         }, this);
11486     },
11487     
11488     showFields : function(items)
11489     {
11490         Roo.each(items, function(i){
11491             
11492             var f = this.findField(i);
11493             
11494             if(!f){
11495                 return;
11496             }
11497             
11498             f.show();
11499             
11500         }, this);
11501     }
11502
11503 });
11504
11505 Roo.apply(Roo.bootstrap.Form, {
11506     
11507     popover : {
11508         
11509         padding : 5,
11510         
11511         isApplied : false,
11512         
11513         isMasked : false,
11514         
11515         form : false,
11516         
11517         target : false,
11518         
11519         toolTip : false,
11520         
11521         intervalID : false,
11522         
11523         maskEl : false,
11524         
11525         apply : function()
11526         {
11527             if(this.isApplied){
11528                 return;
11529             }
11530             
11531             this.maskEl = {
11532                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11533                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11534                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11535                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11536             };
11537             
11538             this.maskEl.top.enableDisplayMode("block");
11539             this.maskEl.left.enableDisplayMode("block");
11540             this.maskEl.bottom.enableDisplayMode("block");
11541             this.maskEl.right.enableDisplayMode("block");
11542             
11543             this.toolTip = new Roo.bootstrap.Tooltip({
11544                 cls : 'roo-form-error-popover',
11545                 alignment : {
11546                     'left' : ['r-l', [-2,0], 'right'],
11547                     'right' : ['l-r', [2,0], 'left'],
11548                     'bottom' : ['tl-bl', [0,2], 'top'],
11549                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11550                 }
11551             });
11552             
11553             this.toolTip.render(Roo.get(document.body));
11554
11555             this.toolTip.el.enableDisplayMode("block");
11556             
11557             Roo.get(document.body).on('click', function(){
11558                 this.unmask();
11559             }, this);
11560             
11561             Roo.get(document.body).on('touchstart', function(){
11562                 this.unmask();
11563             }, this);
11564             
11565             this.isApplied = true
11566         },
11567         
11568         mask : function(form, target)
11569         {
11570             this.form = form;
11571             
11572             this.target = target;
11573             
11574             if(!this.form.errorMask || !target.el){
11575                 return;
11576             }
11577             
11578             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11579             
11580             Roo.log(scrollable);
11581             
11582             var ot = this.target.el.calcOffsetsTo(scrollable);
11583             
11584             var scrollTo = ot[1] - this.form.maskOffset;
11585             
11586             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11587             
11588             scrollable.scrollTo('top', scrollTo);
11589             
11590             var box = this.target.el.getBox();
11591             Roo.log(box);
11592             var zIndex = Roo.bootstrap.Modal.zIndex++;
11593
11594             
11595             this.maskEl.top.setStyle('position', 'absolute');
11596             this.maskEl.top.setStyle('z-index', zIndex);
11597             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11598             this.maskEl.top.setLeft(0);
11599             this.maskEl.top.setTop(0);
11600             this.maskEl.top.show();
11601             
11602             this.maskEl.left.setStyle('position', 'absolute');
11603             this.maskEl.left.setStyle('z-index', zIndex);
11604             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11605             this.maskEl.left.setLeft(0);
11606             this.maskEl.left.setTop(box.y - this.padding);
11607             this.maskEl.left.show();
11608
11609             this.maskEl.bottom.setStyle('position', 'absolute');
11610             this.maskEl.bottom.setStyle('z-index', zIndex);
11611             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11612             this.maskEl.bottom.setLeft(0);
11613             this.maskEl.bottom.setTop(box.bottom + this.padding);
11614             this.maskEl.bottom.show();
11615
11616             this.maskEl.right.setStyle('position', 'absolute');
11617             this.maskEl.right.setStyle('z-index', zIndex);
11618             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11619             this.maskEl.right.setLeft(box.right + this.padding);
11620             this.maskEl.right.setTop(box.y - this.padding);
11621             this.maskEl.right.show();
11622
11623             this.toolTip.bindEl = this.target.el;
11624
11625             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11626
11627             var tip = this.target.blankText;
11628
11629             if(this.target.getValue() !== '' ) {
11630                 
11631                 if (this.target.invalidText.length) {
11632                     tip = this.target.invalidText;
11633                 } else if (this.target.regexText.length){
11634                     tip = this.target.regexText;
11635                 }
11636             }
11637
11638             this.toolTip.show(tip);
11639
11640             this.intervalID = window.setInterval(function() {
11641                 Roo.bootstrap.Form.popover.unmask();
11642             }, 10000);
11643
11644             window.onwheel = function(){ return false;};
11645             
11646             (function(){ this.isMasked = true; }).defer(500, this);
11647             
11648         },
11649         
11650         unmask : function()
11651         {
11652             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11653                 return;
11654             }
11655             
11656             this.maskEl.top.setStyle('position', 'absolute');
11657             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11658             this.maskEl.top.hide();
11659
11660             this.maskEl.left.setStyle('position', 'absolute');
11661             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11662             this.maskEl.left.hide();
11663
11664             this.maskEl.bottom.setStyle('position', 'absolute');
11665             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11666             this.maskEl.bottom.hide();
11667
11668             this.maskEl.right.setStyle('position', 'absolute');
11669             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11670             this.maskEl.right.hide();
11671             
11672             this.toolTip.hide();
11673             
11674             this.toolTip.el.hide();
11675             
11676             window.onwheel = function(){ return true;};
11677             
11678             if(this.intervalID){
11679                 window.clearInterval(this.intervalID);
11680                 this.intervalID = false;
11681             }
11682             
11683             this.isMasked = false;
11684             
11685         }
11686         
11687     }
11688     
11689 });
11690
11691 /*
11692  * Based on:
11693  * Ext JS Library 1.1.1
11694  * Copyright(c) 2006-2007, Ext JS, LLC.
11695  *
11696  * Originally Released Under LGPL - original licence link has changed is not relivant.
11697  *
11698  * Fork - LGPL
11699  * <script type="text/javascript">
11700  */
11701 /**
11702  * @class Roo.form.VTypes
11703  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11704  * @singleton
11705  */
11706 Roo.form.VTypes = function(){
11707     // closure these in so they are only created once.
11708     var alpha = /^[a-zA-Z_]+$/;
11709     var alphanum = /^[a-zA-Z0-9_]+$/;
11710     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11711     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11712
11713     // All these messages and functions are configurable
11714     return {
11715         /**
11716          * The function used to validate email addresses
11717          * @param {String} value The email address
11718          */
11719         'email' : function(v){
11720             return email.test(v);
11721         },
11722         /**
11723          * The error text to display when the email validation function returns false
11724          * @type String
11725          */
11726         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11727         /**
11728          * The keystroke filter mask to be applied on email input
11729          * @type RegExp
11730          */
11731         'emailMask' : /[a-z0-9_\.\-@]/i,
11732
11733         /**
11734          * The function used to validate URLs
11735          * @param {String} value The URL
11736          */
11737         'url' : function(v){
11738             return url.test(v);
11739         },
11740         /**
11741          * The error text to display when the url validation function returns false
11742          * @type String
11743          */
11744         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11745         
11746         /**
11747          * The function used to validate alpha values
11748          * @param {String} value The value
11749          */
11750         'alpha' : function(v){
11751             return alpha.test(v);
11752         },
11753         /**
11754          * The error text to display when the alpha validation function returns false
11755          * @type String
11756          */
11757         'alphaText' : 'This field should only contain letters and _',
11758         /**
11759          * The keystroke filter mask to be applied on alpha input
11760          * @type RegExp
11761          */
11762         'alphaMask' : /[a-z_]/i,
11763
11764         /**
11765          * The function used to validate alphanumeric values
11766          * @param {String} value The value
11767          */
11768         'alphanum' : function(v){
11769             return alphanum.test(v);
11770         },
11771         /**
11772          * The error text to display when the alphanumeric validation function returns false
11773          * @type String
11774          */
11775         'alphanumText' : 'This field should only contain letters, numbers and _',
11776         /**
11777          * The keystroke filter mask to be applied on alphanumeric input
11778          * @type RegExp
11779          */
11780         'alphanumMask' : /[a-z0-9_]/i
11781     };
11782 }();/*
11783  * - LGPL
11784  *
11785  * Input
11786  * 
11787  */
11788
11789 /**
11790  * @class Roo.bootstrap.Input
11791  * @extends Roo.bootstrap.Component
11792  * Bootstrap Input class
11793  * @cfg {Boolean} disabled is it disabled
11794  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11795  * @cfg {String} name name of the input
11796  * @cfg {string} fieldLabel - the label associated
11797  * @cfg {string} placeholder - placeholder to put in text.
11798  * @cfg {string}  before - input group add on before
11799  * @cfg {string} after - input group add on after
11800  * @cfg {string} size - (lg|sm) or leave empty..
11801  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11802  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11803  * @cfg {Number} md colspan out of 12 for computer-sized screens
11804  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11805  * @cfg {string} value default value of the input
11806  * @cfg {Number} labelWidth set the width of label 
11807  * @cfg {Number} labellg set the width of label (1-12)
11808  * @cfg {Number} labelmd set the width of label (1-12)
11809  * @cfg {Number} labelsm set the width of label (1-12)
11810  * @cfg {Number} labelxs set the width of label (1-12)
11811  * @cfg {String} labelAlign (top|left)
11812  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11813  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11814  * @cfg {String} indicatorpos (left|right) default left
11815  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11816  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11817  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11818
11819  * @cfg {String} align (left|center|right) Default left
11820  * @cfg {Boolean} forceFeedback (true|false) Default false
11821  * 
11822  * @constructor
11823  * Create a new Input
11824  * @param {Object} config The config object
11825  */
11826
11827 Roo.bootstrap.Input = function(config){
11828     
11829     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11830     
11831     this.addEvents({
11832         /**
11833          * @event focus
11834          * Fires when this field receives input focus.
11835          * @param {Roo.form.Field} this
11836          */
11837         focus : true,
11838         /**
11839          * @event blur
11840          * Fires when this field loses input focus.
11841          * @param {Roo.form.Field} this
11842          */
11843         blur : true,
11844         /**
11845          * @event specialkey
11846          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11847          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11848          * @param {Roo.form.Field} this
11849          * @param {Roo.EventObject} e The event object
11850          */
11851         specialkey : true,
11852         /**
11853          * @event change
11854          * Fires just before the field blurs if the field value has changed.
11855          * @param {Roo.form.Field} this
11856          * @param {Mixed} newValue The new value
11857          * @param {Mixed} oldValue The original value
11858          */
11859         change : true,
11860         /**
11861          * @event invalid
11862          * Fires after the field has been marked as invalid.
11863          * @param {Roo.form.Field} this
11864          * @param {String} msg The validation message
11865          */
11866         invalid : true,
11867         /**
11868          * @event valid
11869          * Fires after the field has been validated with no errors.
11870          * @param {Roo.form.Field} this
11871          */
11872         valid : true,
11873          /**
11874          * @event keyup
11875          * Fires after the key up
11876          * @param {Roo.form.Field} this
11877          * @param {Roo.EventObject}  e The event Object
11878          */
11879         keyup : true,
11880         /**
11881          * @event paste
11882          * Fires after the user pastes into input
11883          * @param {Roo.form.Field} this
11884          * @param {Roo.EventObject}  e The event Object
11885          */
11886         paste : true
11887     });
11888 };
11889
11890 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11891      /**
11892      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11893       automatic validation (defaults to "keyup").
11894      */
11895     validationEvent : "keyup",
11896      /**
11897      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11898      */
11899     validateOnBlur : true,
11900     /**
11901      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11902      */
11903     validationDelay : 250,
11904      /**
11905      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11906      */
11907     focusClass : "x-form-focus",  // not needed???
11908     
11909        
11910     /**
11911      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11912      */
11913     invalidClass : "has-warning",
11914     
11915     /**
11916      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11917      */
11918     validClass : "has-success",
11919     
11920     /**
11921      * @cfg {Boolean} hasFeedback (true|false) default true
11922      */
11923     hasFeedback : true,
11924     
11925     /**
11926      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11927      */
11928     invalidFeedbackClass : "glyphicon-warning-sign",
11929     
11930     /**
11931      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11932      */
11933     validFeedbackClass : "glyphicon-ok",
11934     
11935     /**
11936      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11937      */
11938     selectOnFocus : false,
11939     
11940      /**
11941      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11942      */
11943     maskRe : null,
11944        /**
11945      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11946      */
11947     vtype : null,
11948     
11949       /**
11950      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11951      */
11952     disableKeyFilter : false,
11953     
11954        /**
11955      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11956      */
11957     disabled : false,
11958      /**
11959      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11960      */
11961     allowBlank : true,
11962     /**
11963      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11964      */
11965     blankText : "Please complete this mandatory field",
11966     
11967      /**
11968      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11969      */
11970     minLength : 0,
11971     /**
11972      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11973      */
11974     maxLength : Number.MAX_VALUE,
11975     /**
11976      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11977      */
11978     minLengthText : "The minimum length for this field is {0}",
11979     /**
11980      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11981      */
11982     maxLengthText : "The maximum length for this field is {0}",
11983   
11984     
11985     /**
11986      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11987      * If available, this function will be called only after the basic validators all return true, and will be passed the
11988      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11989      */
11990     validator : null,
11991     /**
11992      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11993      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11994      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11995      */
11996     regex : null,
11997     /**
11998      * @cfg {String} regexText -- Depricated - use Invalid Text
11999      */
12000     regexText : "",
12001     
12002     /**
12003      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12004      */
12005     invalidText : "",
12006     
12007     
12008     
12009     autocomplete: false,
12010     
12011     
12012     fieldLabel : '',
12013     inputType : 'text',
12014     
12015     name : false,
12016     placeholder: false,
12017     before : false,
12018     after : false,
12019     size : false,
12020     hasFocus : false,
12021     preventMark: false,
12022     isFormField : true,
12023     value : '',
12024     labelWidth : 2,
12025     labelAlign : false,
12026     readOnly : false,
12027     align : false,
12028     formatedValue : false,
12029     forceFeedback : false,
12030     
12031     indicatorpos : 'left',
12032     
12033     labellg : 0,
12034     labelmd : 0,
12035     labelsm : 0,
12036     labelxs : 0,
12037     
12038     capture : '',
12039     accept : '',
12040     
12041     parentLabelAlign : function()
12042     {
12043         var parent = this;
12044         while (parent.parent()) {
12045             parent = parent.parent();
12046             if (typeof(parent.labelAlign) !='undefined') {
12047                 return parent.labelAlign;
12048             }
12049         }
12050         return 'left';
12051         
12052     },
12053     
12054     getAutoCreate : function()
12055     {
12056         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12057         
12058         var id = Roo.id();
12059         
12060         var cfg = {};
12061         
12062         if(this.inputType != 'hidden'){
12063             cfg.cls = 'form-group' //input-group
12064         }
12065         
12066         var input =  {
12067             tag: 'input',
12068             id : id,
12069             type : this.inputType,
12070             value : this.value,
12071             cls : 'form-control',
12072             placeholder : this.placeholder || '',
12073             autocomplete : this.autocomplete || 'new-password'
12074         };
12075         if (this.inputType == 'file') {
12076             input.style = 'overflow:hidden'; // why not in CSS?
12077         }
12078         
12079         if(this.capture.length){
12080             input.capture = this.capture;
12081         }
12082         
12083         if(this.accept.length){
12084             input.accept = this.accept + "/*";
12085         }
12086         
12087         if(this.align){
12088             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12089         }
12090         
12091         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12092             input.maxLength = this.maxLength;
12093         }
12094         
12095         if (this.disabled) {
12096             input.disabled=true;
12097         }
12098         
12099         if (this.readOnly) {
12100             input.readonly=true;
12101         }
12102         
12103         if (this.name) {
12104             input.name = this.name;
12105         }
12106         
12107         if (this.size) {
12108             input.cls += ' input-' + this.size;
12109         }
12110         
12111         var settings=this;
12112         ['xs','sm','md','lg'].map(function(size){
12113             if (settings[size]) {
12114                 cfg.cls += ' col-' + size + '-' + settings[size];
12115             }
12116         });
12117         
12118         var inputblock = input;
12119         
12120         var feedback = {
12121             tag: 'span',
12122             cls: 'glyphicon form-control-feedback'
12123         };
12124             
12125         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12126             
12127             inputblock = {
12128                 cls : 'has-feedback',
12129                 cn :  [
12130                     input,
12131                     feedback
12132                 ] 
12133             };  
12134         }
12135         
12136         if (this.before || this.after) {
12137             
12138             inputblock = {
12139                 cls : 'input-group',
12140                 cn :  [] 
12141             };
12142             
12143             if (this.before && typeof(this.before) == 'string') {
12144                 
12145                 inputblock.cn.push({
12146                     tag :'span',
12147                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12148                     html : this.before
12149                 });
12150             }
12151             if (this.before && typeof(this.before) == 'object') {
12152                 this.before = Roo.factory(this.before);
12153                 
12154                 inputblock.cn.push({
12155                     tag :'span',
12156                     cls : 'roo-input-before input-group-prepend   input-group-' +
12157                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12158                 });
12159             }
12160             
12161             inputblock.cn.push(input);
12162             
12163             if (this.after && typeof(this.after) == 'string') {
12164                 inputblock.cn.push({
12165                     tag :'span',
12166                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12167                     html : this.after
12168                 });
12169             }
12170             if (this.after && typeof(this.after) == 'object') {
12171                 this.after = Roo.factory(this.after);
12172                 
12173                 inputblock.cn.push({
12174                     tag :'span',
12175                     cls : 'roo-input-after input-group-append  input-group-' +
12176                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12177                 });
12178             }
12179             
12180             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12181                 inputblock.cls += ' has-feedback';
12182                 inputblock.cn.push(feedback);
12183             }
12184         };
12185         var indicator = {
12186             tag : 'i',
12187             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12188             tooltip : 'This field is required'
12189         };
12190         if (this.allowBlank ) {
12191             indicator.style = this.allowBlank ? ' display:none' : '';
12192         }
12193         if (align ==='left' && this.fieldLabel.length) {
12194             
12195             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12196             
12197             cfg.cn = [
12198                 indicator,
12199                 {
12200                     tag: 'label',
12201                     'for' :  id,
12202                     cls : 'control-label col-form-label',
12203                     html : this.fieldLabel
12204
12205                 },
12206                 {
12207                     cls : "", 
12208                     cn: [
12209                         inputblock
12210                     ]
12211                 }
12212             ];
12213             
12214             var labelCfg = cfg.cn[1];
12215             var contentCfg = cfg.cn[2];
12216             
12217             if(this.indicatorpos == 'right'){
12218                 cfg.cn = [
12219                     {
12220                         tag: 'label',
12221                         'for' :  id,
12222                         cls : 'control-label col-form-label',
12223                         cn : [
12224                             {
12225                                 tag : 'span',
12226                                 html : this.fieldLabel
12227                             },
12228                             indicator
12229                         ]
12230                     },
12231                     {
12232                         cls : "",
12233                         cn: [
12234                             inputblock
12235                         ]
12236                     }
12237
12238                 ];
12239                 
12240                 labelCfg = cfg.cn[0];
12241                 contentCfg = cfg.cn[1];
12242             
12243             }
12244             
12245             if(this.labelWidth > 12){
12246                 labelCfg.style = "width: " + this.labelWidth + 'px';
12247             }
12248             
12249             if(this.labelWidth < 13 && this.labelmd == 0){
12250                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12251             }
12252             
12253             if(this.labellg > 0){
12254                 labelCfg.cls += ' col-lg-' + this.labellg;
12255                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12256             }
12257             
12258             if(this.labelmd > 0){
12259                 labelCfg.cls += ' col-md-' + this.labelmd;
12260                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12261             }
12262             
12263             if(this.labelsm > 0){
12264                 labelCfg.cls += ' col-sm-' + this.labelsm;
12265                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12266             }
12267             
12268             if(this.labelxs > 0){
12269                 labelCfg.cls += ' col-xs-' + this.labelxs;
12270                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12271             }
12272             
12273             
12274         } else if ( this.fieldLabel.length) {
12275                 
12276             
12277             
12278             cfg.cn = [
12279                 {
12280                     tag : 'i',
12281                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12282                     tooltip : 'This field is required',
12283                     style : this.allowBlank ? ' display:none' : '' 
12284                 },
12285                 {
12286                     tag: 'label',
12287                    //cls : 'input-group-addon',
12288                     html : this.fieldLabel
12289
12290                 },
12291
12292                inputblock
12293
12294            ];
12295            
12296            if(this.indicatorpos == 'right'){
12297        
12298                 cfg.cn = [
12299                     {
12300                         tag: 'label',
12301                        //cls : 'input-group-addon',
12302                         html : this.fieldLabel
12303
12304                     },
12305                     {
12306                         tag : 'i',
12307                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12308                         tooltip : 'This field is required',
12309                         style : this.allowBlank ? ' display:none' : '' 
12310                     },
12311
12312                    inputblock
12313
12314                ];
12315
12316             }
12317
12318         } else {
12319             
12320             cfg.cn = [
12321
12322                     inputblock
12323
12324             ];
12325                 
12326                 
12327         };
12328         
12329         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12330            cfg.cls += ' navbar-form';
12331         }
12332         
12333         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12334             // on BS4 we do this only if not form 
12335             cfg.cls += ' navbar-form';
12336             cfg.tag = 'li';
12337         }
12338         
12339         return cfg;
12340         
12341     },
12342     /**
12343      * return the real input element.
12344      */
12345     inputEl: function ()
12346     {
12347         return this.el.select('input.form-control',true).first();
12348     },
12349     
12350     tooltipEl : function()
12351     {
12352         return this.inputEl();
12353     },
12354     
12355     indicatorEl : function()
12356     {
12357         if (Roo.bootstrap.version == 4) {
12358             return false; // not enabled in v4 yet.
12359         }
12360         
12361         var indicator = this.el.select('i.roo-required-indicator',true).first();
12362         
12363         if(!indicator){
12364             return false;
12365         }
12366         
12367         return indicator;
12368         
12369     },
12370     
12371     setDisabled : function(v)
12372     {
12373         var i  = this.inputEl().dom;
12374         if (!v) {
12375             i.removeAttribute('disabled');
12376             return;
12377             
12378         }
12379         i.setAttribute('disabled','true');
12380     },
12381     initEvents : function()
12382     {
12383           
12384         this.inputEl().on("keydown" , this.fireKey,  this);
12385         this.inputEl().on("focus", this.onFocus,  this);
12386         this.inputEl().on("blur", this.onBlur,  this);
12387         
12388         this.inputEl().relayEvent('keyup', this);
12389         this.inputEl().relayEvent('paste', this);
12390         
12391         this.indicator = this.indicatorEl();
12392         
12393         if(this.indicator){
12394             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12395         }
12396  
12397         // reference to original value for reset
12398         this.originalValue = this.getValue();
12399         //Roo.form.TextField.superclass.initEvents.call(this);
12400         if(this.validationEvent == 'keyup'){
12401             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12402             this.inputEl().on('keyup', this.filterValidation, this);
12403         }
12404         else if(this.validationEvent !== false){
12405             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12406         }
12407         
12408         if(this.selectOnFocus){
12409             this.on("focus", this.preFocus, this);
12410             
12411         }
12412         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12413             this.inputEl().on("keypress", this.filterKeys, this);
12414         } else {
12415             this.inputEl().relayEvent('keypress', this);
12416         }
12417        /* if(this.grow){
12418             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12419             this.el.on("click", this.autoSize,  this);
12420         }
12421         */
12422         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12423             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12424         }
12425         
12426         if (typeof(this.before) == 'object') {
12427             this.before.render(this.el.select('.roo-input-before',true).first());
12428         }
12429         if (typeof(this.after) == 'object') {
12430             this.after.render(this.el.select('.roo-input-after',true).first());
12431         }
12432         
12433         this.inputEl().on('change', this.onChange, this);
12434         
12435     },
12436     filterValidation : function(e){
12437         if(!e.isNavKeyPress()){
12438             this.validationTask.delay(this.validationDelay);
12439         }
12440     },
12441      /**
12442      * Validates the field value
12443      * @return {Boolean} True if the value is valid, else false
12444      */
12445     validate : function(){
12446         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12447         if(this.disabled || this.validateValue(this.getRawValue())){
12448             this.markValid();
12449             return true;
12450         }
12451         
12452         this.markInvalid();
12453         return false;
12454     },
12455     
12456     
12457     /**
12458      * Validates a value according to the field's validation rules and marks the field as invalid
12459      * if the validation fails
12460      * @param {Mixed} value The value to validate
12461      * @return {Boolean} True if the value is valid, else false
12462      */
12463     validateValue : function(value)
12464     {
12465         if(this.getVisibilityEl().hasClass('hidden')){
12466             return true;
12467         }
12468         
12469         if(value.length < 1)  { // if it's blank
12470             if(this.allowBlank){
12471                 return true;
12472             }
12473             return false;
12474         }
12475         
12476         if(value.length < this.minLength){
12477             return false;
12478         }
12479         if(value.length > this.maxLength){
12480             return false;
12481         }
12482         if(this.vtype){
12483             var vt = Roo.form.VTypes;
12484             if(!vt[this.vtype](value, this)){
12485                 return false;
12486             }
12487         }
12488         if(typeof this.validator == "function"){
12489             var msg = this.validator(value);
12490             if(msg !== true){
12491                 return false;
12492             }
12493             if (typeof(msg) == 'string') {
12494                 this.invalidText = msg;
12495             }
12496         }
12497         
12498         if(this.regex && !this.regex.test(value)){
12499             return false;
12500         }
12501         
12502         return true;
12503     },
12504     
12505      // private
12506     fireKey : function(e){
12507         //Roo.log('field ' + e.getKey());
12508         if(e.isNavKeyPress()){
12509             this.fireEvent("specialkey", this, e);
12510         }
12511     },
12512     focus : function (selectText){
12513         if(this.rendered){
12514             this.inputEl().focus();
12515             if(selectText === true){
12516                 this.inputEl().dom.select();
12517             }
12518         }
12519         return this;
12520     } ,
12521     
12522     onFocus : function(){
12523         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12524            // this.el.addClass(this.focusClass);
12525         }
12526         if(!this.hasFocus){
12527             this.hasFocus = true;
12528             this.startValue = this.getValue();
12529             this.fireEvent("focus", this);
12530         }
12531     },
12532     
12533     beforeBlur : Roo.emptyFn,
12534
12535     
12536     // private
12537     onBlur : function(){
12538         this.beforeBlur();
12539         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12540             //this.el.removeClass(this.focusClass);
12541         }
12542         this.hasFocus = false;
12543         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12544             this.validate();
12545         }
12546         var v = this.getValue();
12547         if(String(v) !== String(this.startValue)){
12548             this.fireEvent('change', this, v, this.startValue);
12549         }
12550         this.fireEvent("blur", this);
12551     },
12552     
12553     onChange : function(e)
12554     {
12555         var v = this.getValue();
12556         if(String(v) !== String(this.startValue)){
12557             this.fireEvent('change', this, v, this.startValue);
12558         }
12559         
12560     },
12561     
12562     /**
12563      * Resets the current field value to the originally loaded value and clears any validation messages
12564      */
12565     reset : function(){
12566         this.setValue(this.originalValue);
12567         this.validate();
12568     },
12569      /**
12570      * Returns the name of the field
12571      * @return {Mixed} name The name field
12572      */
12573     getName: function(){
12574         return this.name;
12575     },
12576      /**
12577      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12578      * @return {Mixed} value The field value
12579      */
12580     getValue : function(){
12581         
12582         var v = this.inputEl().getValue();
12583         
12584         return v;
12585     },
12586     /**
12587      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12588      * @return {Mixed} value The field value
12589      */
12590     getRawValue : function(){
12591         var v = this.inputEl().getValue();
12592         
12593         return v;
12594     },
12595     
12596     /**
12597      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12598      * @param {Mixed} value The value to set
12599      */
12600     setRawValue : function(v){
12601         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12602     },
12603     
12604     selectText : function(start, end){
12605         var v = this.getRawValue();
12606         if(v.length > 0){
12607             start = start === undefined ? 0 : start;
12608             end = end === undefined ? v.length : end;
12609             var d = this.inputEl().dom;
12610             if(d.setSelectionRange){
12611                 d.setSelectionRange(start, end);
12612             }else if(d.createTextRange){
12613                 var range = d.createTextRange();
12614                 range.moveStart("character", start);
12615                 range.moveEnd("character", v.length-end);
12616                 range.select();
12617             }
12618         }
12619     },
12620     
12621     /**
12622      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12623      * @param {Mixed} value The value to set
12624      */
12625     setValue : function(v){
12626         this.value = v;
12627         if(this.rendered){
12628             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12629             this.validate();
12630         }
12631     },
12632     
12633     /*
12634     processValue : function(value){
12635         if(this.stripCharsRe){
12636             var newValue = value.replace(this.stripCharsRe, '');
12637             if(newValue !== value){
12638                 this.setRawValue(newValue);
12639                 return newValue;
12640             }
12641         }
12642         return value;
12643     },
12644   */
12645     preFocus : function(){
12646         
12647         if(this.selectOnFocus){
12648             this.inputEl().dom.select();
12649         }
12650     },
12651     filterKeys : function(e){
12652         var k = e.getKey();
12653         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12654             return;
12655         }
12656         var c = e.getCharCode(), cc = String.fromCharCode(c);
12657         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12658             return;
12659         }
12660         if(!this.maskRe.test(cc)){
12661             e.stopEvent();
12662         }
12663     },
12664      /**
12665      * Clear any invalid styles/messages for this field
12666      */
12667     clearInvalid : function(){
12668         
12669         if(!this.el || this.preventMark){ // not rendered
12670             return;
12671         }
12672         
12673         
12674         this.el.removeClass([this.invalidClass, 'is-invalid']);
12675         
12676         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12677             
12678             var feedback = this.el.select('.form-control-feedback', true).first();
12679             
12680             if(feedback){
12681                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12682             }
12683             
12684         }
12685         
12686         if(this.indicator){
12687             this.indicator.removeClass('visible');
12688             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12689         }
12690         
12691         this.fireEvent('valid', this);
12692     },
12693     
12694      /**
12695      * Mark this field as valid
12696      */
12697     markValid : function()
12698     {
12699         if(!this.el  || this.preventMark){ // not rendered...
12700             return;
12701         }
12702         
12703         this.el.removeClass([this.invalidClass, this.validClass]);
12704         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12705
12706         var feedback = this.el.select('.form-control-feedback', true).first();
12707             
12708         if(feedback){
12709             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12710         }
12711         
12712         if(this.indicator){
12713             this.indicator.removeClass('visible');
12714             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12715         }
12716         
12717         if(this.disabled){
12718             return;
12719         }
12720         
12721            
12722         if(this.allowBlank && !this.getRawValue().length){
12723             return;
12724         }
12725         if (Roo.bootstrap.version == 3) {
12726             this.el.addClass(this.validClass);
12727         } else {
12728             this.inputEl().addClass('is-valid');
12729         }
12730
12731         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12732             
12733             var feedback = this.el.select('.form-control-feedback', true).first();
12734             
12735             if(feedback){
12736                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12737                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12738             }
12739             
12740         }
12741         
12742         this.fireEvent('valid', this);
12743     },
12744     
12745      /**
12746      * Mark this field as invalid
12747      * @param {String} msg The validation message
12748      */
12749     markInvalid : function(msg)
12750     {
12751         if(!this.el  || this.preventMark){ // not rendered
12752             return;
12753         }
12754         
12755         this.el.removeClass([this.invalidClass, this.validClass]);
12756         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12757         
12758         var feedback = this.el.select('.form-control-feedback', true).first();
12759             
12760         if(feedback){
12761             this.el.select('.form-control-feedback', true).first().removeClass(
12762                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12763         }
12764
12765         if(this.disabled){
12766             return;
12767         }
12768         
12769         if(this.allowBlank && !this.getRawValue().length){
12770             return;
12771         }
12772         
12773         if(this.indicator){
12774             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12775             this.indicator.addClass('visible');
12776         }
12777         if (Roo.bootstrap.version == 3) {
12778             this.el.addClass(this.invalidClass);
12779         } else {
12780             this.inputEl().addClass('is-invalid');
12781         }
12782         
12783         
12784         
12785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12786             
12787             var feedback = this.el.select('.form-control-feedback', true).first();
12788             
12789             if(feedback){
12790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12791                 
12792                 if(this.getValue().length || this.forceFeedback){
12793                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12794                 }
12795                 
12796             }
12797             
12798         }
12799         
12800         this.fireEvent('invalid', this, msg);
12801     },
12802     // private
12803     SafariOnKeyDown : function(event)
12804     {
12805         // this is a workaround for a password hang bug on chrome/ webkit.
12806         if (this.inputEl().dom.type != 'password') {
12807             return;
12808         }
12809         
12810         var isSelectAll = false;
12811         
12812         if(this.inputEl().dom.selectionEnd > 0){
12813             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12814         }
12815         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12816             event.preventDefault();
12817             this.setValue('');
12818             return;
12819         }
12820         
12821         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12822             
12823             event.preventDefault();
12824             // this is very hacky as keydown always get's upper case.
12825             //
12826             var cc = String.fromCharCode(event.getCharCode());
12827             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12828             
12829         }
12830     },
12831     adjustWidth : function(tag, w){
12832         tag = tag.toLowerCase();
12833         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12834             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12835                 if(tag == 'input'){
12836                     return w + 2;
12837                 }
12838                 if(tag == 'textarea'){
12839                     return w-2;
12840                 }
12841             }else if(Roo.isOpera){
12842                 if(tag == 'input'){
12843                     return w + 2;
12844                 }
12845                 if(tag == 'textarea'){
12846                     return w-2;
12847                 }
12848             }
12849         }
12850         return w;
12851     },
12852     
12853     setFieldLabel : function(v)
12854     {
12855         if(!this.rendered){
12856             return;
12857         }
12858         
12859         if(this.indicatorEl()){
12860             var ar = this.el.select('label > span',true);
12861             
12862             if (ar.elements.length) {
12863                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12864                 this.fieldLabel = v;
12865                 return;
12866             }
12867             
12868             var br = this.el.select('label',true);
12869             
12870             if(br.elements.length) {
12871                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12872                 this.fieldLabel = v;
12873                 return;
12874             }
12875             
12876             Roo.log('Cannot Found any of label > span || label in input');
12877             return;
12878         }
12879         
12880         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12881         this.fieldLabel = v;
12882         
12883         
12884     }
12885 });
12886
12887  
12888 /*
12889  * - LGPL
12890  *
12891  * Input
12892  * 
12893  */
12894
12895 /**
12896  * @class Roo.bootstrap.TextArea
12897  * @extends Roo.bootstrap.Input
12898  * Bootstrap TextArea class
12899  * @cfg {Number} cols Specifies the visible width of a text area
12900  * @cfg {Number} rows Specifies the visible number of lines in a text area
12901  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12902  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12903  * @cfg {string} html text
12904  * 
12905  * @constructor
12906  * Create a new TextArea
12907  * @param {Object} config The config object
12908  */
12909
12910 Roo.bootstrap.TextArea = function(config){
12911     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12912    
12913 };
12914
12915 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12916      
12917     cols : false,
12918     rows : 5,
12919     readOnly : false,
12920     warp : 'soft',
12921     resize : false,
12922     value: false,
12923     html: false,
12924     
12925     getAutoCreate : function(){
12926         
12927         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12928         
12929         var id = Roo.id();
12930         
12931         var cfg = {};
12932         
12933         if(this.inputType != 'hidden'){
12934             cfg.cls = 'form-group' //input-group
12935         }
12936         
12937         var input =  {
12938             tag: 'textarea',
12939             id : id,
12940             warp : this.warp,
12941             rows : this.rows,
12942             value : this.value || '',
12943             html: this.html || '',
12944             cls : 'form-control',
12945             placeholder : this.placeholder || '' 
12946             
12947         };
12948         
12949         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12950             input.maxLength = this.maxLength;
12951         }
12952         
12953         if(this.resize){
12954             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12955         }
12956         
12957         if(this.cols){
12958             input.cols = this.cols;
12959         }
12960         
12961         if (this.readOnly) {
12962             input.readonly = true;
12963         }
12964         
12965         if (this.name) {
12966             input.name = this.name;
12967         }
12968         
12969         if (this.size) {
12970             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12971         }
12972         
12973         var settings=this;
12974         ['xs','sm','md','lg'].map(function(size){
12975             if (settings[size]) {
12976                 cfg.cls += ' col-' + size + '-' + settings[size];
12977             }
12978         });
12979         
12980         var inputblock = input;
12981         
12982         if(this.hasFeedback && !this.allowBlank){
12983             
12984             var feedback = {
12985                 tag: 'span',
12986                 cls: 'glyphicon form-control-feedback'
12987             };
12988
12989             inputblock = {
12990                 cls : 'has-feedback',
12991                 cn :  [
12992                     input,
12993                     feedback
12994                 ] 
12995             };  
12996         }
12997         
12998         
12999         if (this.before || this.after) {
13000             
13001             inputblock = {
13002                 cls : 'input-group',
13003                 cn :  [] 
13004             };
13005             if (this.before) {
13006                 inputblock.cn.push({
13007                     tag :'span',
13008                     cls : 'input-group-addon',
13009                     html : this.before
13010                 });
13011             }
13012             
13013             inputblock.cn.push(input);
13014             
13015             if(this.hasFeedback && !this.allowBlank){
13016                 inputblock.cls += ' has-feedback';
13017                 inputblock.cn.push(feedback);
13018             }
13019             
13020             if (this.after) {
13021                 inputblock.cn.push({
13022                     tag :'span',
13023                     cls : 'input-group-addon',
13024                     html : this.after
13025                 });
13026             }
13027             
13028         }
13029         
13030         if (align ==='left' && this.fieldLabel.length) {
13031             cfg.cn = [
13032                 {
13033                     tag: 'label',
13034                     'for' :  id,
13035                     cls : 'control-label',
13036                     html : this.fieldLabel
13037                 },
13038                 {
13039                     cls : "",
13040                     cn: [
13041                         inputblock
13042                     ]
13043                 }
13044
13045             ];
13046             
13047             if(this.labelWidth > 12){
13048                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13049             }
13050
13051             if(this.labelWidth < 13 && this.labelmd == 0){
13052                 this.labelmd = this.labelWidth;
13053             }
13054
13055             if(this.labellg > 0){
13056                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13057                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13058             }
13059
13060             if(this.labelmd > 0){
13061                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13062                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13063             }
13064
13065             if(this.labelsm > 0){
13066                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13067                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13068             }
13069
13070             if(this.labelxs > 0){
13071                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13072                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13073             }
13074             
13075         } else if ( this.fieldLabel.length) {
13076             cfg.cn = [
13077
13078                {
13079                    tag: 'label',
13080                    //cls : 'input-group-addon',
13081                    html : this.fieldLabel
13082
13083                },
13084
13085                inputblock
13086
13087            ];
13088
13089         } else {
13090
13091             cfg.cn = [
13092
13093                 inputblock
13094
13095             ];
13096                 
13097         }
13098         
13099         if (this.disabled) {
13100             input.disabled=true;
13101         }
13102         
13103         return cfg;
13104         
13105     },
13106     /**
13107      * return the real textarea element.
13108      */
13109     inputEl: function ()
13110     {
13111         return this.el.select('textarea.form-control',true).first();
13112     },
13113     
13114     /**
13115      * Clear any invalid styles/messages for this field
13116      */
13117     clearInvalid : function()
13118     {
13119         
13120         if(!this.el || this.preventMark){ // not rendered
13121             return;
13122         }
13123         
13124         var label = this.el.select('label', true).first();
13125         var icon = this.el.select('i.fa-star', true).first();
13126         
13127         if(label && icon){
13128             icon.remove();
13129         }
13130         this.el.removeClass( this.validClass);
13131         this.inputEl().removeClass('is-invalid');
13132          
13133         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13134             
13135             var feedback = this.el.select('.form-control-feedback', true).first();
13136             
13137             if(feedback){
13138                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13139             }
13140             
13141         }
13142         
13143         this.fireEvent('valid', this);
13144     },
13145     
13146      /**
13147      * Mark this field as valid
13148      */
13149     markValid : function()
13150     {
13151         if(!this.el  || this.preventMark){ // not rendered
13152             return;
13153         }
13154         
13155         this.el.removeClass([this.invalidClass, this.validClass]);
13156         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13157         
13158         var feedback = this.el.select('.form-control-feedback', true).first();
13159             
13160         if(feedback){
13161             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13162         }
13163
13164         if(this.disabled || this.allowBlank){
13165             return;
13166         }
13167         
13168         var label = this.el.select('label', true).first();
13169         var icon = this.el.select('i.fa-star', true).first();
13170         
13171         if(label && icon){
13172             icon.remove();
13173         }
13174         if (Roo.bootstrap.version == 3) {
13175             this.el.addClass(this.validClass);
13176         } else {
13177             this.inputEl().addClass('is-valid');
13178         }
13179         
13180         
13181         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13182             
13183             var feedback = this.el.select('.form-control-feedback', true).first();
13184             
13185             if(feedback){
13186                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13187                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13188             }
13189             
13190         }
13191         
13192         this.fireEvent('valid', this);
13193     },
13194     
13195      /**
13196      * Mark this field as invalid
13197      * @param {String} msg The validation message
13198      */
13199     markInvalid : function(msg)
13200     {
13201         if(!this.el  || this.preventMark){ // not rendered
13202             return;
13203         }
13204         
13205         this.el.removeClass([this.invalidClass, this.validClass]);
13206         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13207         
13208         var feedback = this.el.select('.form-control-feedback', true).first();
13209             
13210         if(feedback){
13211             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13212         }
13213
13214         if(this.disabled || this.allowBlank){
13215             return;
13216         }
13217         
13218         var label = this.el.select('label', true).first();
13219         var icon = this.el.select('i.fa-star', true).first();
13220         
13221         if(!this.getValue().length && label && !icon){
13222             this.el.createChild({
13223                 tag : 'i',
13224                 cls : 'text-danger fa fa-lg fa-star',
13225                 tooltip : 'This field is required',
13226                 style : 'margin-right:5px;'
13227             }, label, true);
13228         }
13229         
13230         if (Roo.bootstrap.version == 3) {
13231             this.el.addClass(this.invalidClass);
13232         } else {
13233             this.inputEl().addClass('is-invalid');
13234         }
13235         
13236         // fixme ... this may be depricated need to test..
13237         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13238             
13239             var feedback = this.el.select('.form-control-feedback', true).first();
13240             
13241             if(feedback){
13242                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13243                 
13244                 if(this.getValue().length || this.forceFeedback){
13245                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13246                 }
13247                 
13248             }
13249             
13250         }
13251         
13252         this.fireEvent('invalid', this, msg);
13253     }
13254 });
13255
13256  
13257 /*
13258  * - LGPL
13259  *
13260  * trigger field - base class for combo..
13261  * 
13262  */
13263  
13264 /**
13265  * @class Roo.bootstrap.TriggerField
13266  * @extends Roo.bootstrap.Input
13267  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13268  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13269  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13270  * for which you can provide a custom implementation.  For example:
13271  * <pre><code>
13272 var trigger = new Roo.bootstrap.TriggerField();
13273 trigger.onTriggerClick = myTriggerFn;
13274 trigger.applyTo('my-field');
13275 </code></pre>
13276  *
13277  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13278  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13279  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13280  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13281  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13282
13283  * @constructor
13284  * Create a new TriggerField.
13285  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13286  * to the base TextField)
13287  */
13288 Roo.bootstrap.TriggerField = function(config){
13289     this.mimicing = false;
13290     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13291 };
13292
13293 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13294     /**
13295      * @cfg {String} triggerClass A CSS class to apply to the trigger
13296      */
13297      /**
13298      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13299      */
13300     hideTrigger:false,
13301
13302     /**
13303      * @cfg {Boolean} removable (true|false) special filter default false
13304      */
13305     removable : false,
13306     
13307     /** @cfg {Boolean} grow @hide */
13308     /** @cfg {Number} growMin @hide */
13309     /** @cfg {Number} growMax @hide */
13310
13311     /**
13312      * @hide 
13313      * @method
13314      */
13315     autoSize: Roo.emptyFn,
13316     // private
13317     monitorTab : true,
13318     // private
13319     deferHeight : true,
13320
13321     
13322     actionMode : 'wrap',
13323     
13324     caret : false,
13325     
13326     
13327     getAutoCreate : function(){
13328        
13329         var align = this.labelAlign || this.parentLabelAlign();
13330         
13331         var id = Roo.id();
13332         
13333         var cfg = {
13334             cls: 'form-group' //input-group
13335         };
13336         
13337         
13338         var input =  {
13339             tag: 'input',
13340             id : id,
13341             type : this.inputType,
13342             cls : 'form-control',
13343             autocomplete: 'new-password',
13344             placeholder : this.placeholder || '' 
13345             
13346         };
13347         if (this.name) {
13348             input.name = this.name;
13349         }
13350         if (this.size) {
13351             input.cls += ' input-' + this.size;
13352         }
13353         
13354         if (this.disabled) {
13355             input.disabled=true;
13356         }
13357         
13358         var inputblock = input;
13359         
13360         if(this.hasFeedback && !this.allowBlank){
13361             
13362             var feedback = {
13363                 tag: 'span',
13364                 cls: 'glyphicon form-control-feedback'
13365             };
13366             
13367             if(this.removable && !this.editable  ){
13368                 inputblock = {
13369                     cls : 'has-feedback',
13370                     cn :  [
13371                         inputblock,
13372                         {
13373                             tag: 'button',
13374                             html : 'x',
13375                             cls : 'roo-combo-removable-btn close'
13376                         },
13377                         feedback
13378                     ] 
13379                 };
13380             } else {
13381                 inputblock = {
13382                     cls : 'has-feedback',
13383                     cn :  [
13384                         inputblock,
13385                         feedback
13386                     ] 
13387                 };
13388             }
13389
13390         } else {
13391             if(this.removable && !this.editable ){
13392                 inputblock = {
13393                     cls : 'roo-removable',
13394                     cn :  [
13395                         inputblock,
13396                         {
13397                             tag: 'button',
13398                             html : 'x',
13399                             cls : 'roo-combo-removable-btn close'
13400                         }
13401                     ] 
13402                 };
13403             }
13404         }
13405         
13406         if (this.before || this.after) {
13407             
13408             inputblock = {
13409                 cls : 'input-group',
13410                 cn :  [] 
13411             };
13412             if (this.before) {
13413                 inputblock.cn.push({
13414                     tag :'span',
13415                     cls : 'input-group-addon input-group-prepend input-group-text',
13416                     html : this.before
13417                 });
13418             }
13419             
13420             inputblock.cn.push(input);
13421             
13422             if(this.hasFeedback && !this.allowBlank){
13423                 inputblock.cls += ' has-feedback';
13424                 inputblock.cn.push(feedback);
13425             }
13426             
13427             if (this.after) {
13428                 inputblock.cn.push({
13429                     tag :'span',
13430                     cls : 'input-group-addon input-group-append input-group-text',
13431                     html : this.after
13432                 });
13433             }
13434             
13435         };
13436         
13437       
13438         
13439         var ibwrap = inputblock;
13440         
13441         if(this.multiple){
13442             ibwrap = {
13443                 tag: 'ul',
13444                 cls: 'roo-select2-choices',
13445                 cn:[
13446                     {
13447                         tag: 'li',
13448                         cls: 'roo-select2-search-field',
13449                         cn: [
13450
13451                             inputblock
13452                         ]
13453                     }
13454                 ]
13455             };
13456                 
13457         }
13458         
13459         var combobox = {
13460             cls: 'roo-select2-container input-group',
13461             cn: [
13462                  {
13463                     tag: 'input',
13464                     type : 'hidden',
13465                     cls: 'form-hidden-field'
13466                 },
13467                 ibwrap
13468             ]
13469         };
13470         
13471         if(!this.multiple && this.showToggleBtn){
13472             
13473             var caret = {
13474                         tag: 'span',
13475                         cls: 'caret'
13476              };
13477             if (this.caret != false) {
13478                 caret = {
13479                      tag: 'i',
13480                      cls: 'fa fa-' + this.caret
13481                 };
13482                 
13483             }
13484             
13485             combobox.cn.push({
13486                 tag :'span',
13487                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13488                 cn : [
13489                     Roo.bootstrap.version == 3 ? caret : '',
13490                     {
13491                         tag: 'span',
13492                         cls: 'combobox-clear',
13493                         cn  : [
13494                             {
13495                                 tag : 'i',
13496                                 cls: 'icon-remove'
13497                             }
13498                         ]
13499                     }
13500                 ]
13501
13502             })
13503         }
13504         
13505         if(this.multiple){
13506             combobox.cls += ' roo-select2-container-multi';
13507         }
13508          var indicator = {
13509             tag : 'i',
13510             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13511             tooltip : 'This field is required'
13512         };
13513         if (Roo.bootstrap.version == 4) {
13514             indicator = {
13515                 tag : 'i',
13516                 style : 'display:none'
13517             };
13518         }
13519         
13520         
13521         if (align ==='left' && this.fieldLabel.length) {
13522             
13523             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13524
13525             cfg.cn = [
13526                 indicator,
13527                 {
13528                     tag: 'label',
13529                     'for' :  id,
13530                     cls : 'control-label',
13531                     html : this.fieldLabel
13532
13533                 },
13534                 {
13535                     cls : "", 
13536                     cn: [
13537                         combobox
13538                     ]
13539                 }
13540
13541             ];
13542             
13543             var labelCfg = cfg.cn[1];
13544             var contentCfg = cfg.cn[2];
13545             
13546             if(this.indicatorpos == 'right'){
13547                 cfg.cn = [
13548                     {
13549                         tag: 'label',
13550                         'for' :  id,
13551                         cls : 'control-label',
13552                         cn : [
13553                             {
13554                                 tag : 'span',
13555                                 html : this.fieldLabel
13556                             },
13557                             indicator
13558                         ]
13559                     },
13560                     {
13561                         cls : "", 
13562                         cn: [
13563                             combobox
13564                         ]
13565                     }
13566
13567                 ];
13568                 
13569                 labelCfg = cfg.cn[0];
13570                 contentCfg = cfg.cn[1];
13571             }
13572             
13573             if(this.labelWidth > 12){
13574                 labelCfg.style = "width: " + this.labelWidth + 'px';
13575             }
13576             
13577             if(this.labelWidth < 13 && this.labelmd == 0){
13578                 this.labelmd = this.labelWidth;
13579             }
13580             
13581             if(this.labellg > 0){
13582                 labelCfg.cls += ' col-lg-' + this.labellg;
13583                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13584             }
13585             
13586             if(this.labelmd > 0){
13587                 labelCfg.cls += ' col-md-' + this.labelmd;
13588                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13589             }
13590             
13591             if(this.labelsm > 0){
13592                 labelCfg.cls += ' col-sm-' + this.labelsm;
13593                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13594             }
13595             
13596             if(this.labelxs > 0){
13597                 labelCfg.cls += ' col-xs-' + this.labelxs;
13598                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13599             }
13600             
13601         } else if ( this.fieldLabel.length) {
13602 //                Roo.log(" label");
13603             cfg.cn = [
13604                 indicator,
13605                {
13606                    tag: 'label',
13607                    //cls : 'input-group-addon',
13608                    html : this.fieldLabel
13609
13610                },
13611
13612                combobox
13613
13614             ];
13615             
13616             if(this.indicatorpos == 'right'){
13617                 
13618                 cfg.cn = [
13619                     {
13620                        tag: 'label',
13621                        cn : [
13622                            {
13623                                tag : 'span',
13624                                html : this.fieldLabel
13625                            },
13626                            indicator
13627                        ]
13628
13629                     },
13630                     combobox
13631
13632                 ];
13633
13634             }
13635
13636         } else {
13637             
13638 //                Roo.log(" no label && no align");
13639                 cfg = combobox
13640                      
13641                 
13642         }
13643         
13644         var settings=this;
13645         ['xs','sm','md','lg'].map(function(size){
13646             if (settings[size]) {
13647                 cfg.cls += ' col-' + size + '-' + settings[size];
13648             }
13649         });
13650         
13651         return cfg;
13652         
13653     },
13654     
13655     
13656     
13657     // private
13658     onResize : function(w, h){
13659 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13660 //        if(typeof w == 'number'){
13661 //            var x = w - this.trigger.getWidth();
13662 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13663 //            this.trigger.setStyle('left', x+'px');
13664 //        }
13665     },
13666
13667     // private
13668     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13669
13670     // private
13671     getResizeEl : function(){
13672         return this.inputEl();
13673     },
13674
13675     // private
13676     getPositionEl : function(){
13677         return this.inputEl();
13678     },
13679
13680     // private
13681     alignErrorIcon : function(){
13682         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13683     },
13684
13685     // private
13686     initEvents : function(){
13687         
13688         this.createList();
13689         
13690         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13691         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13692         if(!this.multiple && this.showToggleBtn){
13693             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13694             if(this.hideTrigger){
13695                 this.trigger.setDisplayed(false);
13696             }
13697             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13698         }
13699         
13700         if(this.multiple){
13701             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13702         }
13703         
13704         if(this.removable && !this.editable && !this.tickable){
13705             var close = this.closeTriggerEl();
13706             
13707             if(close){
13708                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13709                 close.on('click', this.removeBtnClick, this, close);
13710             }
13711         }
13712         
13713         //this.trigger.addClassOnOver('x-form-trigger-over');
13714         //this.trigger.addClassOnClick('x-form-trigger-click');
13715         
13716         //if(!this.width){
13717         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13718         //}
13719     },
13720     
13721     closeTriggerEl : function()
13722     {
13723         var close = this.el.select('.roo-combo-removable-btn', true).first();
13724         return close ? close : false;
13725     },
13726     
13727     removeBtnClick : function(e, h, el)
13728     {
13729         e.preventDefault();
13730         
13731         if(this.fireEvent("remove", this) !== false){
13732             this.reset();
13733             this.fireEvent("afterremove", this)
13734         }
13735     },
13736     
13737     createList : function()
13738     {
13739         this.list = Roo.get(document.body).createChild({
13740             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13741             cls: 'typeahead typeahead-long dropdown-menu shadow',
13742             style: 'display:none'
13743         });
13744         
13745         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13746         
13747     },
13748
13749     // private
13750     initTrigger : function(){
13751        
13752     },
13753
13754     // private
13755     onDestroy : function(){
13756         if(this.trigger){
13757             this.trigger.removeAllListeners();
13758           //  this.trigger.remove();
13759         }
13760         //if(this.wrap){
13761         //    this.wrap.remove();
13762         //}
13763         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13764     },
13765
13766     // private
13767     onFocus : function(){
13768         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13769         /*
13770         if(!this.mimicing){
13771             this.wrap.addClass('x-trigger-wrap-focus');
13772             this.mimicing = true;
13773             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13774             if(this.monitorTab){
13775                 this.el.on("keydown", this.checkTab, this);
13776             }
13777         }
13778         */
13779     },
13780
13781     // private
13782     checkTab : function(e){
13783         if(e.getKey() == e.TAB){
13784             this.triggerBlur();
13785         }
13786     },
13787
13788     // private
13789     onBlur : function(){
13790         // do nothing
13791     },
13792
13793     // private
13794     mimicBlur : function(e, t){
13795         /*
13796         if(!this.wrap.contains(t) && this.validateBlur()){
13797             this.triggerBlur();
13798         }
13799         */
13800     },
13801
13802     // private
13803     triggerBlur : function(){
13804         this.mimicing = false;
13805         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13806         if(this.monitorTab){
13807             this.el.un("keydown", this.checkTab, this);
13808         }
13809         //this.wrap.removeClass('x-trigger-wrap-focus');
13810         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13811     },
13812
13813     // private
13814     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13815     validateBlur : function(e, t){
13816         return true;
13817     },
13818
13819     // private
13820     onDisable : function(){
13821         this.inputEl().dom.disabled = true;
13822         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13823         //if(this.wrap){
13824         //    this.wrap.addClass('x-item-disabled');
13825         //}
13826     },
13827
13828     // private
13829     onEnable : function(){
13830         this.inputEl().dom.disabled = false;
13831         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13832         //if(this.wrap){
13833         //    this.el.removeClass('x-item-disabled');
13834         //}
13835     },
13836
13837     // private
13838     onShow : function(){
13839         var ae = this.getActionEl();
13840         
13841         if(ae){
13842             ae.dom.style.display = '';
13843             ae.dom.style.visibility = 'visible';
13844         }
13845     },
13846
13847     // private
13848     
13849     onHide : function(){
13850         var ae = this.getActionEl();
13851         ae.dom.style.display = 'none';
13852     },
13853
13854     /**
13855      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13856      * by an implementing function.
13857      * @method
13858      * @param {EventObject} e
13859      */
13860     onTriggerClick : Roo.emptyFn
13861 });
13862  
13863 /*
13864 * Licence: LGPL
13865 */
13866
13867 /**
13868  * @class Roo.bootstrap.CardUploader
13869  * @extends Roo.bootstrap.Button
13870  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13871  * @cfg {Number} errorTimeout default 3000
13872  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13873  * @cfg {Array}  html The button text.
13874
13875  *
13876  * @constructor
13877  * Create a new CardUploader
13878  * @param {Object} config The config object
13879  */
13880
13881 Roo.bootstrap.CardUploader = function(config){
13882     
13883  
13884     
13885     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13886     
13887     
13888     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13889         return r.data.id
13890      });
13891     
13892      this.addEvents({
13893          // raw events
13894         /**
13895          * @event preview
13896          * When a image is clicked on - and needs to display a slideshow or similar..
13897          * @param {Roo.bootstrap.Card} this
13898          * @param {Object} The image information data 
13899          *
13900          */
13901         'preview' : true,
13902          /**
13903          * @event download
13904          * When a the download link is clicked
13905          * @param {Roo.bootstrap.Card} this
13906          * @param {Object} The image information data  contains 
13907          */
13908         'download' : true
13909         
13910     });
13911 };
13912  
13913 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13914     
13915      
13916     errorTimeout : 3000,
13917      
13918     images : false,
13919    
13920     fileCollection : false,
13921     allowBlank : true,
13922     
13923     getAutoCreate : function()
13924     {
13925         
13926         var cfg =  {
13927             cls :'form-group' ,
13928             cn : [
13929                
13930                 {
13931                     tag: 'label',
13932                    //cls : 'input-group-addon',
13933                     html : this.fieldLabel
13934
13935                 },
13936
13937                 {
13938                     tag: 'input',
13939                     type : 'hidden',
13940                     name : this.name,
13941                     value : this.value,
13942                     cls : 'd-none  form-control'
13943                 },
13944                 
13945                 {
13946                     tag: 'input',
13947                     multiple : 'multiple',
13948                     type : 'file',
13949                     cls : 'd-none  roo-card-upload-selector'
13950                 },
13951                 
13952                 {
13953                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13954                 },
13955                 {
13956                     cls : 'card-columns roo-card-uploader-container'
13957                 }
13958
13959             ]
13960         };
13961            
13962          
13963         return cfg;
13964     },
13965     
13966     getChildContainer : function() /// what children are added to.
13967     {
13968         return this.containerEl;
13969     },
13970    
13971     getButtonContainer : function() /// what children are added to.
13972     {
13973         return this.el.select(".roo-card-uploader-button-container").first();
13974     },
13975    
13976     initEvents : function()
13977     {
13978         
13979         Roo.bootstrap.Input.prototype.initEvents.call(this);
13980         
13981         var t = this;
13982         this.addxtype({
13983             xns: Roo.bootstrap,
13984
13985             xtype : 'Button',
13986             container_method : 'getButtonContainer' ,            
13987             html :  this.html, // fix changable?
13988             cls : 'w-100 ',
13989             listeners : {
13990                 'click' : function(btn, e) {
13991                     t.onClick(e);
13992                 }
13993             }
13994         });
13995         
13996         
13997         
13998         
13999         this.urlAPI = (window.createObjectURL && window) || 
14000                                 (window.URL && URL.revokeObjectURL && URL) || 
14001                                 (window.webkitURL && webkitURL);
14002                         
14003          
14004          
14005          
14006         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14007         
14008         this.selectorEl.on('change', this.onFileSelected, this);
14009         if (this.images) {
14010             var t = this;
14011             this.images.forEach(function(img) {
14012                 t.addCard(img)
14013             });
14014             this.images = false;
14015         }
14016         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14017          
14018        
14019     },
14020     
14021    
14022     onClick : function(e)
14023     {
14024         e.preventDefault();
14025          
14026         this.selectorEl.dom.click();
14027          
14028     },
14029     
14030     onFileSelected : function(e)
14031     {
14032         e.preventDefault();
14033         
14034         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14035             return;
14036         }
14037         
14038         Roo.each(this.selectorEl.dom.files, function(file){    
14039             this.addFile(file);
14040         }, this);
14041          
14042     },
14043     
14044       
14045     
14046       
14047     
14048     addFile : function(file)
14049     {
14050            
14051         if(typeof(file) === 'string'){
14052             throw "Add file by name?"; // should not happen
14053             return;
14054         }
14055         
14056         if(!file || !this.urlAPI){
14057             return;
14058         }
14059         
14060         // file;
14061         // file.type;
14062         
14063         var _this = this;
14064         
14065         
14066         var url = _this.urlAPI.createObjectURL( file);
14067            
14068         this.addCard({
14069             id : Roo.bootstrap.CardUploader.ID--,
14070             is_uploaded : false,
14071             src : url,
14072             srcfile : file,
14073             title : file.name,
14074             mimetype : file.type,
14075             preview : false,
14076             is_deleted : 0
14077         });
14078         
14079     },
14080     
14081     /**
14082      * addCard - add an Attachment to the uploader
14083      * @param data - the data about the image to upload
14084      *
14085      * {
14086           id : 123
14087           title : "Title of file",
14088           is_uploaded : false,
14089           src : "http://.....",
14090           srcfile : { the File upload object },
14091           mimetype : file.type,
14092           preview : false,
14093           is_deleted : 0
14094           .. any other data...
14095         }
14096      *
14097      * 
14098     */
14099     
14100     addCard : function (data)
14101     {
14102         // hidden input element?
14103         // if the file is not an image...
14104         //then we need to use something other that and header_image
14105         var t = this;
14106         //   remove.....
14107         var footer = [
14108             {
14109                 xns : Roo.bootstrap,
14110                 xtype : 'CardFooter',
14111                  items: [
14112                     {
14113                         xns : Roo.bootstrap,
14114                         xtype : 'Element',
14115                         cls : 'd-flex',
14116                         items : [
14117                             
14118                             {
14119                                 xns : Roo.bootstrap,
14120                                 xtype : 'Button',
14121                                 html : String.format("<small>{0}</small>", data.title),
14122                                 cls : 'col-10 text-left',
14123                                 size: 'sm',
14124                                 weight: 'link',
14125                                 fa : 'download',
14126                                 listeners : {
14127                                     click : function() {
14128                                      
14129                                         t.fireEvent( "download", t, data );
14130                                     }
14131                                 }
14132                             },
14133                           
14134                             {
14135                                 xns : Roo.bootstrap,
14136                                 xtype : 'Button',
14137                                 style: 'max-height: 28px; ',
14138                                 size : 'sm',
14139                                 weight: 'danger',
14140                                 cls : 'col-2',
14141                                 fa : 'times',
14142                                 listeners : {
14143                                     click : function() {
14144                                         t.removeCard(data.id)
14145                                     }
14146                                 }
14147                             }
14148                         ]
14149                     }
14150                     
14151                 ] 
14152             }
14153             
14154         ];
14155         
14156         var cn = this.addxtype(
14157             {
14158                  
14159                 xns : Roo.bootstrap,
14160                 xtype : 'Card',
14161                 closeable : true,
14162                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14163                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14164                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14165                 data : data,
14166                 html : false,
14167                  
14168                 items : footer,
14169                 initEvents : function() {
14170                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14171                     var card = this;
14172                     this.imgEl = this.el.select('.card-img-top').first();
14173                     if (this.imgEl) {
14174                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14175                         this.imgEl.set({ 'pointer' : 'cursor' });
14176                                   
14177                     }
14178                     this.getCardFooter().addClass('p-1');
14179                     
14180                   
14181                 }
14182                 
14183             }
14184         );
14185         // dont' really need ot update items.
14186         // this.items.push(cn);
14187         this.fileCollection.add(cn);
14188         
14189         if (!data.srcfile) {
14190             this.updateInput();
14191             return;
14192         }
14193             
14194         var _t = this;
14195         var reader = new FileReader();
14196         reader.addEventListener("load", function() {  
14197             data.srcdata =  reader.result;
14198             _t.updateInput();
14199         });
14200         reader.readAsDataURL(data.srcfile);
14201         
14202         
14203         
14204     },
14205     removeCard : function(id)
14206     {
14207         
14208         var card  = this.fileCollection.get(id);
14209         card.data.is_deleted = 1;
14210         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14211         //this.fileCollection.remove(card);
14212         //this.items = this.items.filter(function(e) { return e != card });
14213         // dont' really need ot update items.
14214         card.el.dom.parentNode.removeChild(card.el.dom);
14215         this.updateInput();
14216
14217         
14218     },
14219     reset: function()
14220     {
14221         this.fileCollection.each(function(card) {
14222             if (card.el.dom && card.el.dom.parentNode) {
14223                 card.el.dom.parentNode.removeChild(card.el.dom);
14224             }
14225         });
14226         this.fileCollection.clear();
14227         this.updateInput();
14228     },
14229     
14230     updateInput : function()
14231     {
14232          var data = [];
14233         this.fileCollection.each(function(e) {
14234             data.push(e.data);
14235             
14236         });
14237         this.inputEl().dom.value = JSON.stringify(data);
14238         
14239         
14240         
14241     }
14242     
14243     
14244 });
14245
14246
14247 Roo.bootstrap.CardUploader.ID = -1;/*
14248  * Based on:
14249  * Ext JS Library 1.1.1
14250  * Copyright(c) 2006-2007, Ext JS, LLC.
14251  *
14252  * Originally Released Under LGPL - original licence link has changed is not relivant.
14253  *
14254  * Fork - LGPL
14255  * <script type="text/javascript">
14256  */
14257
14258
14259 /**
14260  * @class Roo.data.SortTypes
14261  * @singleton
14262  * Defines the default sorting (casting?) comparison functions used when sorting data.
14263  */
14264 Roo.data.SortTypes = {
14265     /**
14266      * Default sort that does nothing
14267      * @param {Mixed} s The value being converted
14268      * @return {Mixed} The comparison value
14269      */
14270     none : function(s){
14271         return s;
14272     },
14273     
14274     /**
14275      * The regular expression used to strip tags
14276      * @type {RegExp}
14277      * @property
14278      */
14279     stripTagsRE : /<\/?[^>]+>/gi,
14280     
14281     /**
14282      * Strips all HTML tags to sort on text only
14283      * @param {Mixed} s The value being converted
14284      * @return {String} The comparison value
14285      */
14286     asText : function(s){
14287         return String(s).replace(this.stripTagsRE, "");
14288     },
14289     
14290     /**
14291      * Strips all HTML tags to sort on text only - Case insensitive
14292      * @param {Mixed} s The value being converted
14293      * @return {String} The comparison value
14294      */
14295     asUCText : function(s){
14296         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14297     },
14298     
14299     /**
14300      * Case insensitive string
14301      * @param {Mixed} s The value being converted
14302      * @return {String} The comparison value
14303      */
14304     asUCString : function(s) {
14305         return String(s).toUpperCase();
14306     },
14307     
14308     /**
14309      * Date sorting
14310      * @param {Mixed} s The value being converted
14311      * @return {Number} The comparison value
14312      */
14313     asDate : function(s) {
14314         if(!s){
14315             return 0;
14316         }
14317         if(s instanceof Date){
14318             return s.getTime();
14319         }
14320         return Date.parse(String(s));
14321     },
14322     
14323     /**
14324      * Float sorting
14325      * @param {Mixed} s The value being converted
14326      * @return {Float} The comparison value
14327      */
14328     asFloat : function(s) {
14329         var val = parseFloat(String(s).replace(/,/g, ""));
14330         if(isNaN(val)) {
14331             val = 0;
14332         }
14333         return val;
14334     },
14335     
14336     /**
14337      * Integer sorting
14338      * @param {Mixed} s The value being converted
14339      * @return {Number} The comparison value
14340      */
14341     asInt : function(s) {
14342         var val = parseInt(String(s).replace(/,/g, ""));
14343         if(isNaN(val)) {
14344             val = 0;
14345         }
14346         return val;
14347     }
14348 };/*
14349  * Based on:
14350  * Ext JS Library 1.1.1
14351  * Copyright(c) 2006-2007, Ext JS, LLC.
14352  *
14353  * Originally Released Under LGPL - original licence link has changed is not relivant.
14354  *
14355  * Fork - LGPL
14356  * <script type="text/javascript">
14357  */
14358
14359 /**
14360 * @class Roo.data.Record
14361  * Instances of this class encapsulate both record <em>definition</em> information, and record
14362  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14363  * to access Records cached in an {@link Roo.data.Store} object.<br>
14364  * <p>
14365  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14366  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14367  * objects.<br>
14368  * <p>
14369  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14370  * @constructor
14371  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14372  * {@link #create}. The parameters are the same.
14373  * @param {Array} data An associative Array of data values keyed by the field name.
14374  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14375  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14376  * not specified an integer id is generated.
14377  */
14378 Roo.data.Record = function(data, id){
14379     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14380     this.data = data;
14381 };
14382
14383 /**
14384  * Generate a constructor for a specific record layout.
14385  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14386  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14387  * Each field definition object may contain the following properties: <ul>
14388  * <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,
14389  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14390  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14391  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14392  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14393  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14394  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14395  * this may be omitted.</p></li>
14396  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14397  * <ul><li>auto (Default, implies no conversion)</li>
14398  * <li>string</li>
14399  * <li>int</li>
14400  * <li>float</li>
14401  * <li>boolean</li>
14402  * <li>date</li></ul></p></li>
14403  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14404  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14405  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14406  * by the Reader into an object that will be stored in the Record. It is passed the
14407  * following parameters:<ul>
14408  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14409  * </ul></p></li>
14410  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14411  * </ul>
14412  * <br>usage:<br><pre><code>
14413 var TopicRecord = Roo.data.Record.create(
14414     {name: 'title', mapping: 'topic_title'},
14415     {name: 'author', mapping: 'username'},
14416     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14417     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14418     {name: 'lastPoster', mapping: 'user2'},
14419     {name: 'excerpt', mapping: 'post_text'}
14420 );
14421
14422 var myNewRecord = new TopicRecord({
14423     title: 'Do my job please',
14424     author: 'noobie',
14425     totalPosts: 1,
14426     lastPost: new Date(),
14427     lastPoster: 'Animal',
14428     excerpt: 'No way dude!'
14429 });
14430 myStore.add(myNewRecord);
14431 </code></pre>
14432  * @method create
14433  * @static
14434  */
14435 Roo.data.Record.create = function(o){
14436     var f = function(){
14437         f.superclass.constructor.apply(this, arguments);
14438     };
14439     Roo.extend(f, Roo.data.Record);
14440     var p = f.prototype;
14441     p.fields = new Roo.util.MixedCollection(false, function(field){
14442         return field.name;
14443     });
14444     for(var i = 0, len = o.length; i < len; i++){
14445         p.fields.add(new Roo.data.Field(o[i]));
14446     }
14447     f.getField = function(name){
14448         return p.fields.get(name);  
14449     };
14450     return f;
14451 };
14452
14453 Roo.data.Record.AUTO_ID = 1000;
14454 Roo.data.Record.EDIT = 'edit';
14455 Roo.data.Record.REJECT = 'reject';
14456 Roo.data.Record.COMMIT = 'commit';
14457
14458 Roo.data.Record.prototype = {
14459     /**
14460      * Readonly flag - true if this record has been modified.
14461      * @type Boolean
14462      */
14463     dirty : false,
14464     editing : false,
14465     error: null,
14466     modified: null,
14467
14468     // private
14469     join : function(store){
14470         this.store = store;
14471     },
14472
14473     /**
14474      * Set the named field to the specified value.
14475      * @param {String} name The name of the field to set.
14476      * @param {Object} value The value to set the field to.
14477      */
14478     set : function(name, value){
14479         if(this.data[name] == value){
14480             return;
14481         }
14482         this.dirty = true;
14483         if(!this.modified){
14484             this.modified = {};
14485         }
14486         if(typeof this.modified[name] == 'undefined'){
14487             this.modified[name] = this.data[name];
14488         }
14489         this.data[name] = value;
14490         if(!this.editing && this.store){
14491             this.store.afterEdit(this);
14492         }       
14493     },
14494
14495     /**
14496      * Get the value of the named field.
14497      * @param {String} name The name of the field to get the value of.
14498      * @return {Object} The value of the field.
14499      */
14500     get : function(name){
14501         return this.data[name]; 
14502     },
14503
14504     // private
14505     beginEdit : function(){
14506         this.editing = true;
14507         this.modified = {}; 
14508     },
14509
14510     // private
14511     cancelEdit : function(){
14512         this.editing = false;
14513         delete this.modified;
14514     },
14515
14516     // private
14517     endEdit : function(){
14518         this.editing = false;
14519         if(this.dirty && this.store){
14520             this.store.afterEdit(this);
14521         }
14522     },
14523
14524     /**
14525      * Usually called by the {@link Roo.data.Store} which owns the Record.
14526      * Rejects all changes made to the Record since either creation, or the last commit operation.
14527      * Modified fields are reverted to their original values.
14528      * <p>
14529      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14530      * of reject operations.
14531      */
14532     reject : function(){
14533         var m = this.modified;
14534         for(var n in m){
14535             if(typeof m[n] != "function"){
14536                 this.data[n] = m[n];
14537             }
14538         }
14539         this.dirty = false;
14540         delete this.modified;
14541         this.editing = false;
14542         if(this.store){
14543             this.store.afterReject(this);
14544         }
14545     },
14546
14547     /**
14548      * Usually called by the {@link Roo.data.Store} which owns the Record.
14549      * Commits all changes made to the Record since either creation, or the last commit operation.
14550      * <p>
14551      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14552      * of commit operations.
14553      */
14554     commit : function(){
14555         this.dirty = false;
14556         delete this.modified;
14557         this.editing = false;
14558         if(this.store){
14559             this.store.afterCommit(this);
14560         }
14561     },
14562
14563     // private
14564     hasError : function(){
14565         return this.error != null;
14566     },
14567
14568     // private
14569     clearError : function(){
14570         this.error = null;
14571     },
14572
14573     /**
14574      * Creates a copy of this record.
14575      * @param {String} id (optional) A new record id if you don't want to use this record's id
14576      * @return {Record}
14577      */
14578     copy : function(newId) {
14579         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14580     }
14581 };/*
14582  * Based on:
14583  * Ext JS Library 1.1.1
14584  * Copyright(c) 2006-2007, Ext JS, LLC.
14585  *
14586  * Originally Released Under LGPL - original licence link has changed is not relivant.
14587  *
14588  * Fork - LGPL
14589  * <script type="text/javascript">
14590  */
14591
14592
14593
14594 /**
14595  * @class Roo.data.Store
14596  * @extends Roo.util.Observable
14597  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14598  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14599  * <p>
14600  * 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
14601  * has no knowledge of the format of the data returned by the Proxy.<br>
14602  * <p>
14603  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14604  * instances from the data object. These records are cached and made available through accessor functions.
14605  * @constructor
14606  * Creates a new Store.
14607  * @param {Object} config A config object containing the objects needed for the Store to access data,
14608  * and read the data into Records.
14609  */
14610 Roo.data.Store = function(config){
14611     this.data = new Roo.util.MixedCollection(false);
14612     this.data.getKey = function(o){
14613         return o.id;
14614     };
14615     this.baseParams = {};
14616     // private
14617     this.paramNames = {
14618         "start" : "start",
14619         "limit" : "limit",
14620         "sort" : "sort",
14621         "dir" : "dir",
14622         "multisort" : "_multisort"
14623     };
14624
14625     if(config && config.data){
14626         this.inlineData = config.data;
14627         delete config.data;
14628     }
14629
14630     Roo.apply(this, config);
14631     
14632     if(this.reader){ // reader passed
14633         this.reader = Roo.factory(this.reader, Roo.data);
14634         this.reader.xmodule = this.xmodule || false;
14635         if(!this.recordType){
14636             this.recordType = this.reader.recordType;
14637         }
14638         if(this.reader.onMetaChange){
14639             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14640         }
14641     }
14642
14643     if(this.recordType){
14644         this.fields = this.recordType.prototype.fields;
14645     }
14646     this.modified = [];
14647
14648     this.addEvents({
14649         /**
14650          * @event datachanged
14651          * Fires when the data cache has changed, and a widget which is using this Store
14652          * as a Record cache should refresh its view.
14653          * @param {Store} this
14654          */
14655         datachanged : true,
14656         /**
14657          * @event metachange
14658          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14659          * @param {Store} this
14660          * @param {Object} meta The JSON metadata
14661          */
14662         metachange : true,
14663         /**
14664          * @event add
14665          * Fires when Records have been added to the Store
14666          * @param {Store} this
14667          * @param {Roo.data.Record[]} records The array of Records added
14668          * @param {Number} index The index at which the record(s) were added
14669          */
14670         add : true,
14671         /**
14672          * @event remove
14673          * Fires when a Record has been removed from the Store
14674          * @param {Store} this
14675          * @param {Roo.data.Record} record The Record that was removed
14676          * @param {Number} index The index at which the record was removed
14677          */
14678         remove : true,
14679         /**
14680          * @event update
14681          * Fires when a Record has been updated
14682          * @param {Store} this
14683          * @param {Roo.data.Record} record The Record that was updated
14684          * @param {String} operation The update operation being performed.  Value may be one of:
14685          * <pre><code>
14686  Roo.data.Record.EDIT
14687  Roo.data.Record.REJECT
14688  Roo.data.Record.COMMIT
14689          * </code></pre>
14690          */
14691         update : true,
14692         /**
14693          * @event clear
14694          * Fires when the data cache has been cleared.
14695          * @param {Store} this
14696          */
14697         clear : true,
14698         /**
14699          * @event beforeload
14700          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14701          * the load action will be canceled.
14702          * @param {Store} this
14703          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14704          */
14705         beforeload : true,
14706         /**
14707          * @event beforeloadadd
14708          * Fires after a new set of Records has been loaded.
14709          * @param {Store} this
14710          * @param {Roo.data.Record[]} records The Records that were loaded
14711          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14712          */
14713         beforeloadadd : true,
14714         /**
14715          * @event load
14716          * Fires after a new set of Records has been loaded, before they are added to the store.
14717          * @param {Store} this
14718          * @param {Roo.data.Record[]} records The Records that were loaded
14719          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14720          * @params {Object} return from reader
14721          */
14722         load : true,
14723         /**
14724          * @event loadexception
14725          * Fires if an exception occurs in the Proxy during loading.
14726          * Called with the signature of the Proxy's "loadexception" event.
14727          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14728          * 
14729          * @param {Proxy} 
14730          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14731          * @param {Object} load options 
14732          * @param {Object} jsonData from your request (normally this contains the Exception)
14733          */
14734         loadexception : true
14735     });
14736     
14737     if(this.proxy){
14738         this.proxy = Roo.factory(this.proxy, Roo.data);
14739         this.proxy.xmodule = this.xmodule || false;
14740         this.relayEvents(this.proxy,  ["loadexception"]);
14741     }
14742     this.sortToggle = {};
14743     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14744
14745     Roo.data.Store.superclass.constructor.call(this);
14746
14747     if(this.inlineData){
14748         this.loadData(this.inlineData);
14749         delete this.inlineData;
14750     }
14751 };
14752
14753 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14754      /**
14755     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14756     * without a remote query - used by combo/forms at present.
14757     */
14758     
14759     /**
14760     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
14761     */
14762     /**
14763     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14764     */
14765     /**
14766     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
14767     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14768     */
14769     /**
14770     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14771     * on any HTTP request
14772     */
14773     /**
14774     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14775     */
14776     /**
14777     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14778     */
14779     multiSort: false,
14780     /**
14781     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14782     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14783     */
14784     remoteSort : false,
14785
14786     /**
14787     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14788      * loaded or when a record is removed. (defaults to false).
14789     */
14790     pruneModifiedRecords : false,
14791
14792     // private
14793     lastOptions : null,
14794
14795     /**
14796      * Add Records to the Store and fires the add event.
14797      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14798      */
14799     add : function(records){
14800         records = [].concat(records);
14801         for(var i = 0, len = records.length; i < len; i++){
14802             records[i].join(this);
14803         }
14804         var index = this.data.length;
14805         this.data.addAll(records);
14806         this.fireEvent("add", this, records, index);
14807     },
14808
14809     /**
14810      * Remove a Record from the Store and fires the remove event.
14811      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14812      */
14813     remove : function(record){
14814         var index = this.data.indexOf(record);
14815         this.data.removeAt(index);
14816  
14817         if(this.pruneModifiedRecords){
14818             this.modified.remove(record);
14819         }
14820         this.fireEvent("remove", this, record, index);
14821     },
14822
14823     /**
14824      * Remove all Records from the Store and fires the clear event.
14825      */
14826     removeAll : function(){
14827         this.data.clear();
14828         if(this.pruneModifiedRecords){
14829             this.modified = [];
14830         }
14831         this.fireEvent("clear", this);
14832     },
14833
14834     /**
14835      * Inserts Records to the Store at the given index and fires the add event.
14836      * @param {Number} index The start index at which to insert the passed Records.
14837      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14838      */
14839     insert : function(index, records){
14840         records = [].concat(records);
14841         for(var i = 0, len = records.length; i < len; i++){
14842             this.data.insert(index, records[i]);
14843             records[i].join(this);
14844         }
14845         this.fireEvent("add", this, records, index);
14846     },
14847
14848     /**
14849      * Get the index within the cache of the passed Record.
14850      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14851      * @return {Number} The index of the passed Record. Returns -1 if not found.
14852      */
14853     indexOf : function(record){
14854         return this.data.indexOf(record);
14855     },
14856
14857     /**
14858      * Get the index within the cache of the Record with the passed id.
14859      * @param {String} id The id of the Record to find.
14860      * @return {Number} The index of the Record. Returns -1 if not found.
14861      */
14862     indexOfId : function(id){
14863         return this.data.indexOfKey(id);
14864     },
14865
14866     /**
14867      * Get the Record with the specified id.
14868      * @param {String} id The id of the Record to find.
14869      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14870      */
14871     getById : function(id){
14872         return this.data.key(id);
14873     },
14874
14875     /**
14876      * Get the Record at the specified index.
14877      * @param {Number} index The index of the Record to find.
14878      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14879      */
14880     getAt : function(index){
14881         return this.data.itemAt(index);
14882     },
14883
14884     /**
14885      * Returns a range of Records between specified indices.
14886      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14887      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14888      * @return {Roo.data.Record[]} An array of Records
14889      */
14890     getRange : function(start, end){
14891         return this.data.getRange(start, end);
14892     },
14893
14894     // private
14895     storeOptions : function(o){
14896         o = Roo.apply({}, o);
14897         delete o.callback;
14898         delete o.scope;
14899         this.lastOptions = o;
14900     },
14901
14902     /**
14903      * Loads the Record cache from the configured Proxy using the configured Reader.
14904      * <p>
14905      * If using remote paging, then the first load call must specify the <em>start</em>
14906      * and <em>limit</em> properties in the options.params property to establish the initial
14907      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14908      * <p>
14909      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14910      * and this call will return before the new data has been loaded. Perform any post-processing
14911      * in a callback function, or in a "load" event handler.</strong>
14912      * <p>
14913      * @param {Object} options An object containing properties which control loading options:<ul>
14914      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14915      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14916      * passed the following arguments:<ul>
14917      * <li>r : Roo.data.Record[]</li>
14918      * <li>options: Options object from the load call</li>
14919      * <li>success: Boolean success indicator</li></ul></li>
14920      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14921      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14922      * </ul>
14923      */
14924     load : function(options){
14925         options = options || {};
14926         if(this.fireEvent("beforeload", this, options) !== false){
14927             this.storeOptions(options);
14928             var p = Roo.apply(options.params || {}, this.baseParams);
14929             // if meta was not loaded from remote source.. try requesting it.
14930             if (!this.reader.metaFromRemote) {
14931                 p._requestMeta = 1;
14932             }
14933             if(this.sortInfo && this.remoteSort){
14934                 var pn = this.paramNames;
14935                 p[pn["sort"]] = this.sortInfo.field;
14936                 p[pn["dir"]] = this.sortInfo.direction;
14937             }
14938             if (this.multiSort) {
14939                 var pn = this.paramNames;
14940                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14941             }
14942             
14943             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14944         }
14945     },
14946
14947     /**
14948      * Reloads the Record cache from the configured Proxy using the configured Reader and
14949      * the options from the last load operation performed.
14950      * @param {Object} options (optional) An object containing properties which may override the options
14951      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14952      * the most recently used options are reused).
14953      */
14954     reload : function(options){
14955         this.load(Roo.applyIf(options||{}, this.lastOptions));
14956     },
14957
14958     // private
14959     // Called as a callback by the Reader during a load operation.
14960     loadRecords : function(o, options, success){
14961         if(!o || success === false){
14962             if(success !== false){
14963                 this.fireEvent("load", this, [], options, o);
14964             }
14965             if(options.callback){
14966                 options.callback.call(options.scope || this, [], options, false);
14967             }
14968             return;
14969         }
14970         // if data returned failure - throw an exception.
14971         if (o.success === false) {
14972             // show a message if no listener is registered.
14973             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14974                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14975             }
14976             // loadmask wil be hooked into this..
14977             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14978             return;
14979         }
14980         var r = o.records, t = o.totalRecords || r.length;
14981         
14982         this.fireEvent("beforeloadadd", this, r, options, o);
14983         
14984         if(!options || options.add !== true){
14985             if(this.pruneModifiedRecords){
14986                 this.modified = [];
14987             }
14988             for(var i = 0, len = r.length; i < len; i++){
14989                 r[i].join(this);
14990             }
14991             if(this.snapshot){
14992                 this.data = this.snapshot;
14993                 delete this.snapshot;
14994             }
14995             this.data.clear();
14996             this.data.addAll(r);
14997             this.totalLength = t;
14998             this.applySort();
14999             this.fireEvent("datachanged", this);
15000         }else{
15001             this.totalLength = Math.max(t, this.data.length+r.length);
15002             this.add(r);
15003         }
15004         
15005         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15006                 
15007             var e = new Roo.data.Record({});
15008
15009             e.set(this.parent.displayField, this.parent.emptyTitle);
15010             e.set(this.parent.valueField, '');
15011
15012             this.insert(0, e);
15013         }
15014             
15015         this.fireEvent("load", this, r, options, o);
15016         if(options.callback){
15017             options.callback.call(options.scope || this, r, options, true);
15018         }
15019     },
15020
15021
15022     /**
15023      * Loads data from a passed data block. A Reader which understands the format of the data
15024      * must have been configured in the constructor.
15025      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15026      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15027      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15028      */
15029     loadData : function(o, append){
15030         var r = this.reader.readRecords(o);
15031         this.loadRecords(r, {add: append}, true);
15032     },
15033     
15034      /**
15035      * using 'cn' the nested child reader read the child array into it's child stores.
15036      * @param {Object} rec The record with a 'children array
15037      */
15038     loadDataFromChildren : function(rec)
15039     {
15040         this.loadData(this.reader.toLoadData(rec));
15041     },
15042     
15043
15044     /**
15045      * Gets the number of cached records.
15046      * <p>
15047      * <em>If using paging, this may not be the total size of the dataset. If the data object
15048      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15049      * the data set size</em>
15050      */
15051     getCount : function(){
15052         return this.data.length || 0;
15053     },
15054
15055     /**
15056      * Gets the total number of records in the dataset as returned by the server.
15057      * <p>
15058      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15059      * the dataset size</em>
15060      */
15061     getTotalCount : function(){
15062         return this.totalLength || 0;
15063     },
15064
15065     /**
15066      * Returns the sort state of the Store as an object with two properties:
15067      * <pre><code>
15068  field {String} The name of the field by which the Records are sorted
15069  direction {String} The sort order, "ASC" or "DESC"
15070      * </code></pre>
15071      */
15072     getSortState : function(){
15073         return this.sortInfo;
15074     },
15075
15076     // private
15077     applySort : function(){
15078         if(this.sortInfo && !this.remoteSort){
15079             var s = this.sortInfo, f = s.field;
15080             var st = this.fields.get(f).sortType;
15081             var fn = function(r1, r2){
15082                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15083                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15084             };
15085             this.data.sort(s.direction, fn);
15086             if(this.snapshot && this.snapshot != this.data){
15087                 this.snapshot.sort(s.direction, fn);
15088             }
15089         }
15090     },
15091
15092     /**
15093      * Sets the default sort column and order to be used by the next load operation.
15094      * @param {String} fieldName The name of the field to sort by.
15095      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15096      */
15097     setDefaultSort : function(field, dir){
15098         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15099     },
15100
15101     /**
15102      * Sort the Records.
15103      * If remote sorting is used, the sort is performed on the server, and the cache is
15104      * reloaded. If local sorting is used, the cache is sorted internally.
15105      * @param {String} fieldName The name of the field to sort by.
15106      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15107      */
15108     sort : function(fieldName, dir){
15109         var f = this.fields.get(fieldName);
15110         if(!dir){
15111             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15112             
15113             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15114                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15115             }else{
15116                 dir = f.sortDir;
15117             }
15118         }
15119         this.sortToggle[f.name] = dir;
15120         this.sortInfo = {field: f.name, direction: dir};
15121         if(!this.remoteSort){
15122             this.applySort();
15123             this.fireEvent("datachanged", this);
15124         }else{
15125             this.load(this.lastOptions);
15126         }
15127     },
15128
15129     /**
15130      * Calls the specified function for each of the Records in the cache.
15131      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15132      * Returning <em>false</em> aborts and exits the iteration.
15133      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15134      */
15135     each : function(fn, scope){
15136         this.data.each(fn, scope);
15137     },
15138
15139     /**
15140      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15141      * (e.g., during paging).
15142      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15143      */
15144     getModifiedRecords : function(){
15145         return this.modified;
15146     },
15147
15148     // private
15149     createFilterFn : function(property, value, anyMatch){
15150         if(!value.exec){ // not a regex
15151             value = String(value);
15152             if(value.length == 0){
15153                 return false;
15154             }
15155             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15156         }
15157         return function(r){
15158             return value.test(r.data[property]);
15159         };
15160     },
15161
15162     /**
15163      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15164      * @param {String} property A field on your records
15165      * @param {Number} start The record index to start at (defaults to 0)
15166      * @param {Number} end The last record index to include (defaults to length - 1)
15167      * @return {Number} The sum
15168      */
15169     sum : function(property, start, end){
15170         var rs = this.data.items, v = 0;
15171         start = start || 0;
15172         end = (end || end === 0) ? end : rs.length-1;
15173
15174         for(var i = start; i <= end; i++){
15175             v += (rs[i].data[property] || 0);
15176         }
15177         return v;
15178     },
15179
15180     /**
15181      * Filter the records by a specified property.
15182      * @param {String} field A field on your records
15183      * @param {String/RegExp} value Either a string that the field
15184      * should start with or a RegExp to test against the field
15185      * @param {Boolean} anyMatch True to match any part not just the beginning
15186      */
15187     filter : function(property, value, anyMatch){
15188         var fn = this.createFilterFn(property, value, anyMatch);
15189         return fn ? this.filterBy(fn) : this.clearFilter();
15190     },
15191
15192     /**
15193      * Filter by a function. The specified function will be called with each
15194      * record in this data source. If the function returns true the record is included,
15195      * otherwise it is filtered.
15196      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15197      * @param {Object} scope (optional) The scope of the function (defaults to this)
15198      */
15199     filterBy : function(fn, scope){
15200         this.snapshot = this.snapshot || this.data;
15201         this.data = this.queryBy(fn, scope||this);
15202         this.fireEvent("datachanged", this);
15203     },
15204
15205     /**
15206      * Query the records by a specified property.
15207      * @param {String} field A field on your records
15208      * @param {String/RegExp} value Either a string that the field
15209      * should start with or a RegExp to test against the field
15210      * @param {Boolean} anyMatch True to match any part not just the beginning
15211      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15212      */
15213     query : function(property, value, anyMatch){
15214         var fn = this.createFilterFn(property, value, anyMatch);
15215         return fn ? this.queryBy(fn) : this.data.clone();
15216     },
15217
15218     /**
15219      * Query by a function. The specified function will be called with each
15220      * record in this data source. If the function returns true the record is included
15221      * in the results.
15222      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15223      * @param {Object} scope (optional) The scope of the function (defaults to this)
15224       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15225      **/
15226     queryBy : function(fn, scope){
15227         var data = this.snapshot || this.data;
15228         return data.filterBy(fn, scope||this);
15229     },
15230
15231     /**
15232      * Collects unique values for a particular dataIndex from this store.
15233      * @param {String} dataIndex The property to collect
15234      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15235      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15236      * @return {Array} An array of the unique values
15237      **/
15238     collect : function(dataIndex, allowNull, bypassFilter){
15239         var d = (bypassFilter === true && this.snapshot) ?
15240                 this.snapshot.items : this.data.items;
15241         var v, sv, r = [], l = {};
15242         for(var i = 0, len = d.length; i < len; i++){
15243             v = d[i].data[dataIndex];
15244             sv = String(v);
15245             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15246                 l[sv] = true;
15247                 r[r.length] = v;
15248             }
15249         }
15250         return r;
15251     },
15252
15253     /**
15254      * Revert to a view of the Record cache with no filtering applied.
15255      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15256      */
15257     clearFilter : function(suppressEvent){
15258         if(this.snapshot && this.snapshot != this.data){
15259             this.data = this.snapshot;
15260             delete this.snapshot;
15261             if(suppressEvent !== true){
15262                 this.fireEvent("datachanged", this);
15263             }
15264         }
15265     },
15266
15267     // private
15268     afterEdit : function(record){
15269         if(this.modified.indexOf(record) == -1){
15270             this.modified.push(record);
15271         }
15272         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15273     },
15274     
15275     // private
15276     afterReject : function(record){
15277         this.modified.remove(record);
15278         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15279     },
15280
15281     // private
15282     afterCommit : function(record){
15283         this.modified.remove(record);
15284         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15285     },
15286
15287     /**
15288      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15289      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15290      */
15291     commitChanges : function(){
15292         var m = this.modified.slice(0);
15293         this.modified = [];
15294         for(var i = 0, len = m.length; i < len; i++){
15295             m[i].commit();
15296         }
15297     },
15298
15299     /**
15300      * Cancel outstanding changes on all changed records.
15301      */
15302     rejectChanges : function(){
15303         var m = this.modified.slice(0);
15304         this.modified = [];
15305         for(var i = 0, len = m.length; i < len; i++){
15306             m[i].reject();
15307         }
15308     },
15309
15310     onMetaChange : function(meta, rtype, o){
15311         this.recordType = rtype;
15312         this.fields = rtype.prototype.fields;
15313         delete this.snapshot;
15314         this.sortInfo = meta.sortInfo || this.sortInfo;
15315         this.modified = [];
15316         this.fireEvent('metachange', this, this.reader.meta);
15317     },
15318     
15319     moveIndex : function(data, type)
15320     {
15321         var index = this.indexOf(data);
15322         
15323         var newIndex = index + type;
15324         
15325         this.remove(data);
15326         
15327         this.insert(newIndex, data);
15328         
15329     }
15330 });/*
15331  * Based on:
15332  * Ext JS Library 1.1.1
15333  * Copyright(c) 2006-2007, Ext JS, LLC.
15334  *
15335  * Originally Released Under LGPL - original licence link has changed is not relivant.
15336  *
15337  * Fork - LGPL
15338  * <script type="text/javascript">
15339  */
15340
15341 /**
15342  * @class Roo.data.SimpleStore
15343  * @extends Roo.data.Store
15344  * Small helper class to make creating Stores from Array data easier.
15345  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15346  * @cfg {Array} fields An array of field definition objects, or field name strings.
15347  * @cfg {Object} an existing reader (eg. copied from another store)
15348  * @cfg {Array} data The multi-dimensional array of data
15349  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15350  * @cfg {Roo.data.Reader} reader  [not-required] 
15351  * @constructor
15352  * @param {Object} config
15353  */
15354 Roo.data.SimpleStore = function(config)
15355 {
15356     Roo.data.SimpleStore.superclass.constructor.call(this, {
15357         isLocal : true,
15358         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15359                 id: config.id
15360             },
15361             Roo.data.Record.create(config.fields)
15362         ),
15363         proxy : new Roo.data.MemoryProxy(config.data)
15364     });
15365     this.load();
15366 };
15367 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15368  * Based on:
15369  * Ext JS Library 1.1.1
15370  * Copyright(c) 2006-2007, Ext JS, LLC.
15371  *
15372  * Originally Released Under LGPL - original licence link has changed is not relivant.
15373  *
15374  * Fork - LGPL
15375  * <script type="text/javascript">
15376  */
15377
15378 /**
15379 /**
15380  * @extends Roo.data.Store
15381  * @class Roo.data.JsonStore
15382  * Small helper class to make creating Stores for JSON data easier. <br/>
15383 <pre><code>
15384 var store = new Roo.data.JsonStore({
15385     url: 'get-images.php',
15386     root: 'images',
15387     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15388 });
15389 </code></pre>
15390  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15391  * JsonReader and HttpProxy (unless inline data is provided).</b>
15392  * @cfg {Array} fields An array of field definition objects, or field name strings.
15393  * @constructor
15394  * @param {Object} config
15395  */
15396 Roo.data.JsonStore = function(c){
15397     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15398         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15399         reader: new Roo.data.JsonReader(c, c.fields)
15400     }));
15401 };
15402 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15403  * Based on:
15404  * Ext JS Library 1.1.1
15405  * Copyright(c) 2006-2007, Ext JS, LLC.
15406  *
15407  * Originally Released Under LGPL - original licence link has changed is not relivant.
15408  *
15409  * Fork - LGPL
15410  * <script type="text/javascript">
15411  */
15412
15413  
15414 Roo.data.Field = function(config){
15415     if(typeof config == "string"){
15416         config = {name: config};
15417     }
15418     Roo.apply(this, config);
15419     
15420     if(!this.type){
15421         this.type = "auto";
15422     }
15423     
15424     var st = Roo.data.SortTypes;
15425     // named sortTypes are supported, here we look them up
15426     if(typeof this.sortType == "string"){
15427         this.sortType = st[this.sortType];
15428     }
15429     
15430     // set default sortType for strings and dates
15431     if(!this.sortType){
15432         switch(this.type){
15433             case "string":
15434                 this.sortType = st.asUCString;
15435                 break;
15436             case "date":
15437                 this.sortType = st.asDate;
15438                 break;
15439             default:
15440                 this.sortType = st.none;
15441         }
15442     }
15443
15444     // define once
15445     var stripRe = /[\$,%]/g;
15446
15447     // prebuilt conversion function for this field, instead of
15448     // switching every time we're reading a value
15449     if(!this.convert){
15450         var cv, dateFormat = this.dateFormat;
15451         switch(this.type){
15452             case "":
15453             case "auto":
15454             case undefined:
15455                 cv = function(v){ return v; };
15456                 break;
15457             case "string":
15458                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15459                 break;
15460             case "int":
15461                 cv = function(v){
15462                     return v !== undefined && v !== null && v !== '' ?
15463                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15464                     };
15465                 break;
15466             case "float":
15467                 cv = function(v){
15468                     return v !== undefined && v !== null && v !== '' ?
15469                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15470                     };
15471                 break;
15472             case "bool":
15473             case "boolean":
15474                 cv = function(v){ return v === true || v === "true" || v == 1; };
15475                 break;
15476             case "date":
15477                 cv = function(v){
15478                     if(!v){
15479                         return '';
15480                     }
15481                     if(v instanceof Date){
15482                         return v;
15483                     }
15484                     if(dateFormat){
15485                         if(dateFormat == "timestamp"){
15486                             return new Date(v*1000);
15487                         }
15488                         return Date.parseDate(v, dateFormat);
15489                     }
15490                     var parsed = Date.parse(v);
15491                     return parsed ? new Date(parsed) : null;
15492                 };
15493              break;
15494             
15495         }
15496         this.convert = cv;
15497     }
15498 };
15499
15500 Roo.data.Field.prototype = {
15501     dateFormat: null,
15502     defaultValue: "",
15503     mapping: null,
15504     sortType : null,
15505     sortDir : "ASC"
15506 };/*
15507  * Based on:
15508  * Ext JS Library 1.1.1
15509  * Copyright(c) 2006-2007, Ext JS, LLC.
15510  *
15511  * Originally Released Under LGPL - original licence link has changed is not relivant.
15512  *
15513  * Fork - LGPL
15514  * <script type="text/javascript">
15515  */
15516  
15517 // Base class for reading structured data from a data source.  This class is intended to be
15518 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15519
15520 /**
15521  * @class Roo.data.DataReader
15522  * @abstract
15523  * Base class for reading structured data from a data source.  This class is intended to be
15524  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15525  */
15526
15527 Roo.data.DataReader = function(meta, recordType){
15528     
15529     this.meta = meta;
15530     
15531     this.recordType = recordType instanceof Array ? 
15532         Roo.data.Record.create(recordType) : recordType;
15533 };
15534
15535 Roo.data.DataReader.prototype = {
15536     
15537     
15538     readerType : 'Data',
15539      /**
15540      * Create an empty record
15541      * @param {Object} data (optional) - overlay some values
15542      * @return {Roo.data.Record} record created.
15543      */
15544     newRow :  function(d) {
15545         var da =  {};
15546         this.recordType.prototype.fields.each(function(c) {
15547             switch( c.type) {
15548                 case 'int' : da[c.name] = 0; break;
15549                 case 'date' : da[c.name] = new Date(); break;
15550                 case 'float' : da[c.name] = 0.0; break;
15551                 case 'boolean' : da[c.name] = false; break;
15552                 default : da[c.name] = ""; break;
15553             }
15554             
15555         });
15556         return new this.recordType(Roo.apply(da, d));
15557     }
15558     
15559     
15560 };/*
15561  * Based on:
15562  * Ext JS Library 1.1.1
15563  * Copyright(c) 2006-2007, Ext JS, LLC.
15564  *
15565  * Originally Released Under LGPL - original licence link has changed is not relivant.
15566  *
15567  * Fork - LGPL
15568  * <script type="text/javascript">
15569  */
15570
15571 /**
15572  * @class Roo.data.DataProxy
15573  * @extends Roo.data.Observable
15574  * @abstract
15575  * This class is an abstract base class for implementations which provide retrieval of
15576  * unformatted data objects.<br>
15577  * <p>
15578  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15579  * (of the appropriate type which knows how to parse the data object) to provide a block of
15580  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15581  * <p>
15582  * Custom implementations must implement the load method as described in
15583  * {@link Roo.data.HttpProxy#load}.
15584  */
15585 Roo.data.DataProxy = function(){
15586     this.addEvents({
15587         /**
15588          * @event beforeload
15589          * Fires before a network request is made to retrieve a data object.
15590          * @param {Object} This DataProxy object.
15591          * @param {Object} params The params parameter to the load function.
15592          */
15593         beforeload : true,
15594         /**
15595          * @event load
15596          * Fires before the load method's callback is called.
15597          * @param {Object} This DataProxy object.
15598          * @param {Object} o The data object.
15599          * @param {Object} arg The callback argument object passed to the load function.
15600          */
15601         load : true,
15602         /**
15603          * @event loadexception
15604          * Fires if an Exception occurs during data retrieval.
15605          * @param {Object} This DataProxy object.
15606          * @param {Object} o The data object.
15607          * @param {Object} arg The callback argument object passed to the load function.
15608          * @param {Object} e The Exception.
15609          */
15610         loadexception : true
15611     });
15612     Roo.data.DataProxy.superclass.constructor.call(this);
15613 };
15614
15615 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15616
15617     /**
15618      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15619      */
15620 /*
15621  * Based on:
15622  * Ext JS Library 1.1.1
15623  * Copyright(c) 2006-2007, Ext JS, LLC.
15624  *
15625  * Originally Released Under LGPL - original licence link has changed is not relivant.
15626  *
15627  * Fork - LGPL
15628  * <script type="text/javascript">
15629  */
15630 /**
15631  * @class Roo.data.MemoryProxy
15632  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15633  * to the Reader when its load method is called.
15634  * @constructor
15635  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15636  */
15637 Roo.data.MemoryProxy = function(data){
15638     if (data.data) {
15639         data = data.data;
15640     }
15641     Roo.data.MemoryProxy.superclass.constructor.call(this);
15642     this.data = data;
15643 };
15644
15645 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15646     
15647     /**
15648      * Load data from the requested source (in this case an in-memory
15649      * data object passed to the constructor), read the data object into
15650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15651      * process that block using the passed callback.
15652      * @param {Object} params This parameter is not used by the MemoryProxy class.
15653      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15654      * object into a block of Roo.data.Records.
15655      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15656      * The function must be passed <ul>
15657      * <li>The Record block object</li>
15658      * <li>The "arg" argument from the load function</li>
15659      * <li>A boolean success indicator</li>
15660      * </ul>
15661      * @param {Object} scope The scope in which to call the callback
15662      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15663      */
15664     load : function(params, reader, callback, scope, arg){
15665         params = params || {};
15666         var result;
15667         try {
15668             result = reader.readRecords(params.data ? params.data :this.data);
15669         }catch(e){
15670             this.fireEvent("loadexception", this, arg, null, e);
15671             callback.call(scope, null, arg, false);
15672             return;
15673         }
15674         callback.call(scope, result, arg, true);
15675     },
15676     
15677     // private
15678     update : function(params, records){
15679         
15680     }
15681 });/*
15682  * Based on:
15683  * Ext JS Library 1.1.1
15684  * Copyright(c) 2006-2007, Ext JS, LLC.
15685  *
15686  * Originally Released Under LGPL - original licence link has changed is not relivant.
15687  *
15688  * Fork - LGPL
15689  * <script type="text/javascript">
15690  */
15691 /**
15692  * @class Roo.data.HttpProxy
15693  * @extends Roo.data.DataProxy
15694  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15695  * configured to reference a certain URL.<br><br>
15696  * <p>
15697  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15698  * from which the running page was served.<br><br>
15699  * <p>
15700  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15701  * <p>
15702  * Be aware that to enable the browser to parse an XML document, the server must set
15703  * the Content-Type header in the HTTP response to "text/xml".
15704  * @constructor
15705  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15706  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15707  * will be used to make the request.
15708  */
15709 Roo.data.HttpProxy = function(conn){
15710     Roo.data.HttpProxy.superclass.constructor.call(this);
15711     // is conn a conn config or a real conn?
15712     this.conn = conn;
15713     this.useAjax = !conn || !conn.events;
15714   
15715 };
15716
15717 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15718     // thse are take from connection...
15719     
15720     /**
15721      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15722      */
15723     /**
15724      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15725      * extra parameters to each request made by this object. (defaults to undefined)
15726      */
15727     /**
15728      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15729      *  to each request made by this object. (defaults to undefined)
15730      */
15731     /**
15732      * @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)
15733      */
15734     /**
15735      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15736      */
15737      /**
15738      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15739      * @type Boolean
15740      */
15741   
15742
15743     /**
15744      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15745      * @type Boolean
15746      */
15747     /**
15748      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15749      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15750      * a finer-grained basis than the DataProxy events.
15751      */
15752     getConnection : function(){
15753         return this.useAjax ? Roo.Ajax : this.conn;
15754     },
15755
15756     /**
15757      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15758      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15759      * process that block using the passed callback.
15760      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15761      * for the request to the remote server.
15762      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15763      * object into a block of Roo.data.Records.
15764      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15765      * The function must be passed <ul>
15766      * <li>The Record block object</li>
15767      * <li>The "arg" argument from the load function</li>
15768      * <li>A boolean success indicator</li>
15769      * </ul>
15770      * @param {Object} scope The scope in which to call the callback
15771      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15772      */
15773     load : function(params, reader, callback, scope, arg){
15774         if(this.fireEvent("beforeload", this, params) !== false){
15775             var  o = {
15776                 params : params || {},
15777                 request: {
15778                     callback : callback,
15779                     scope : scope,
15780                     arg : arg
15781                 },
15782                 reader: reader,
15783                 callback : this.loadResponse,
15784                 scope: this
15785             };
15786             if(this.useAjax){
15787                 Roo.applyIf(o, this.conn);
15788                 if(this.activeRequest){
15789                     Roo.Ajax.abort(this.activeRequest);
15790                 }
15791                 this.activeRequest = Roo.Ajax.request(o);
15792             }else{
15793                 this.conn.request(o);
15794             }
15795         }else{
15796             callback.call(scope||this, null, arg, false);
15797         }
15798     },
15799
15800     // private
15801     loadResponse : function(o, success, response){
15802         delete this.activeRequest;
15803         if(!success){
15804             this.fireEvent("loadexception", this, o, response);
15805             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15806             return;
15807         }
15808         var result;
15809         try {
15810             result = o.reader.read(response);
15811         }catch(e){
15812             this.fireEvent("loadexception", this, o, response, e);
15813             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15814             return;
15815         }
15816         
15817         this.fireEvent("load", this, o, o.request.arg);
15818         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15819     },
15820
15821     // private
15822     update : function(dataSet){
15823
15824     },
15825
15826     // private
15827     updateResponse : function(dataSet){
15828
15829     }
15830 });/*
15831  * Based on:
15832  * Ext JS Library 1.1.1
15833  * Copyright(c) 2006-2007, Ext JS, LLC.
15834  *
15835  * Originally Released Under LGPL - original licence link has changed is not relivant.
15836  *
15837  * Fork - LGPL
15838  * <script type="text/javascript">
15839  */
15840
15841 /**
15842  * @class Roo.data.ScriptTagProxy
15843  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15844  * other than the originating domain of the running page.<br><br>
15845  * <p>
15846  * <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
15847  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15848  * <p>
15849  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15850  * source code that is used as the source inside a &lt;script> tag.<br><br>
15851  * <p>
15852  * In order for the browser to process the returned data, the server must wrap the data object
15853  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15854  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15855  * depending on whether the callback name was passed:
15856  * <p>
15857  * <pre><code>
15858 boolean scriptTag = false;
15859 String cb = request.getParameter("callback");
15860 if (cb != null) {
15861     scriptTag = true;
15862     response.setContentType("text/javascript");
15863 } else {
15864     response.setContentType("application/x-json");
15865 }
15866 Writer out = response.getWriter();
15867 if (scriptTag) {
15868     out.write(cb + "(");
15869 }
15870 out.print(dataBlock.toJsonString());
15871 if (scriptTag) {
15872     out.write(");");
15873 }
15874 </pre></code>
15875  *
15876  * @constructor
15877  * @param {Object} config A configuration object.
15878  */
15879 Roo.data.ScriptTagProxy = function(config){
15880     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15881     Roo.apply(this, config);
15882     this.head = document.getElementsByTagName("head")[0];
15883 };
15884
15885 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15886
15887 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15888     /**
15889      * @cfg {String} url The URL from which to request the data object.
15890      */
15891     /**
15892      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15893      */
15894     timeout : 30000,
15895     /**
15896      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15897      * the server the name of the callback function set up by the load call to process the returned data object.
15898      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15899      * javascript output which calls this named function passing the data object as its only parameter.
15900      */
15901     callbackParam : "callback",
15902     /**
15903      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15904      * name to the request.
15905      */
15906     nocache : true,
15907
15908     /**
15909      * Load data from the configured URL, read the data object into
15910      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15911      * process that block using the passed callback.
15912      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15913      * for the request to the remote server.
15914      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15915      * object into a block of Roo.data.Records.
15916      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15917      * The function must be passed <ul>
15918      * <li>The Record block object</li>
15919      * <li>The "arg" argument from the load function</li>
15920      * <li>A boolean success indicator</li>
15921      * </ul>
15922      * @param {Object} scope The scope in which to call the callback
15923      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15924      */
15925     load : function(params, reader, callback, scope, arg){
15926         if(this.fireEvent("beforeload", this, params) !== false){
15927
15928             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15929
15930             var url = this.url;
15931             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15932             if(this.nocache){
15933                 url += "&_dc=" + (new Date().getTime());
15934             }
15935             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15936             var trans = {
15937                 id : transId,
15938                 cb : "stcCallback"+transId,
15939                 scriptId : "stcScript"+transId,
15940                 params : params,
15941                 arg : arg,
15942                 url : url,
15943                 callback : callback,
15944                 scope : scope,
15945                 reader : reader
15946             };
15947             var conn = this;
15948
15949             window[trans.cb] = function(o){
15950                 conn.handleResponse(o, trans);
15951             };
15952
15953             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15954
15955             if(this.autoAbort !== false){
15956                 this.abort();
15957             }
15958
15959             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15960
15961             var script = document.createElement("script");
15962             script.setAttribute("src", url);
15963             script.setAttribute("type", "text/javascript");
15964             script.setAttribute("id", trans.scriptId);
15965             this.head.appendChild(script);
15966
15967             this.trans = trans;
15968         }else{
15969             callback.call(scope||this, null, arg, false);
15970         }
15971     },
15972
15973     // private
15974     isLoading : function(){
15975         return this.trans ? true : false;
15976     },
15977
15978     /**
15979      * Abort the current server request.
15980      */
15981     abort : function(){
15982         if(this.isLoading()){
15983             this.destroyTrans(this.trans);
15984         }
15985     },
15986
15987     // private
15988     destroyTrans : function(trans, isLoaded){
15989         this.head.removeChild(document.getElementById(trans.scriptId));
15990         clearTimeout(trans.timeoutId);
15991         if(isLoaded){
15992             window[trans.cb] = undefined;
15993             try{
15994                 delete window[trans.cb];
15995             }catch(e){}
15996         }else{
15997             // if hasn't been loaded, wait for load to remove it to prevent script error
15998             window[trans.cb] = function(){
15999                 window[trans.cb] = undefined;
16000                 try{
16001                     delete window[trans.cb];
16002                 }catch(e){}
16003             };
16004         }
16005     },
16006
16007     // private
16008     handleResponse : function(o, trans){
16009         this.trans = false;
16010         this.destroyTrans(trans, true);
16011         var result;
16012         try {
16013             result = trans.reader.readRecords(o);
16014         }catch(e){
16015             this.fireEvent("loadexception", this, o, trans.arg, e);
16016             trans.callback.call(trans.scope||window, null, trans.arg, false);
16017             return;
16018         }
16019         this.fireEvent("load", this, o, trans.arg);
16020         trans.callback.call(trans.scope||window, result, trans.arg, true);
16021     },
16022
16023     // private
16024     handleFailure : function(trans){
16025         this.trans = false;
16026         this.destroyTrans(trans, false);
16027         this.fireEvent("loadexception", this, null, trans.arg);
16028         trans.callback.call(trans.scope||window, null, trans.arg, false);
16029     }
16030 });/*
16031  * Based on:
16032  * Ext JS Library 1.1.1
16033  * Copyright(c) 2006-2007, Ext JS, LLC.
16034  *
16035  * Originally Released Under LGPL - original licence link has changed is not relivant.
16036  *
16037  * Fork - LGPL
16038  * <script type="text/javascript">
16039  */
16040
16041 /**
16042  * @class Roo.data.JsonReader
16043  * @extends Roo.data.DataReader
16044  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16045  * based on mappings in a provided Roo.data.Record constructor.
16046  * 
16047  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16048  * in the reply previously. 
16049  * 
16050  * <p>
16051  * Example code:
16052  * <pre><code>
16053 var RecordDef = Roo.data.Record.create([
16054     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16055     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16056 ]);
16057 var myReader = new Roo.data.JsonReader({
16058     totalProperty: "results",    // The property which contains the total dataset size (optional)
16059     root: "rows",                // The property which contains an Array of row objects
16060     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16061 }, RecordDef);
16062 </code></pre>
16063  * <p>
16064  * This would consume a JSON file like this:
16065  * <pre><code>
16066 { 'results': 2, 'rows': [
16067     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16068     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16069 }
16070 </code></pre>
16071  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16072  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16073  * paged from the remote server.
16074  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16075  * @cfg {String} root name of the property which contains the Array of row objects.
16076  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16077  * @cfg {Array} fields Array of field definition objects
16078  * @constructor
16079  * Create a new JsonReader
16080  * @param {Object} meta Metadata configuration options
16081  * @param {Object} recordType Either an Array of field definition objects,
16082  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16083  */
16084 Roo.data.JsonReader = function(meta, recordType){
16085     
16086     meta = meta || {};
16087     // set some defaults:
16088     Roo.applyIf(meta, {
16089         totalProperty: 'total',
16090         successProperty : 'success',
16091         root : 'data',
16092         id : 'id'
16093     });
16094     
16095     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16096 };
16097 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16098     
16099     readerType : 'Json',
16100     
16101     /**
16102      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16103      * Used by Store query builder to append _requestMeta to params.
16104      * 
16105      */
16106     metaFromRemote : false,
16107     /**
16108      * This method is only used by a DataProxy which has retrieved data from a remote server.
16109      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16110      * @return {Object} data A data block which is used by an Roo.data.Store object as
16111      * a cache of Roo.data.Records.
16112      */
16113     read : function(response){
16114         var json = response.responseText;
16115        
16116         var o = /* eval:var:o */ eval("("+json+")");
16117         if(!o) {
16118             throw {message: "JsonReader.read: Json object not found"};
16119         }
16120         
16121         if(o.metaData){
16122             
16123             delete this.ef;
16124             this.metaFromRemote = true;
16125             this.meta = o.metaData;
16126             this.recordType = Roo.data.Record.create(o.metaData.fields);
16127             this.onMetaChange(this.meta, this.recordType, o);
16128         }
16129         return this.readRecords(o);
16130     },
16131
16132     // private function a store will implement
16133     onMetaChange : function(meta, recordType, o){
16134
16135     },
16136
16137     /**
16138          * @ignore
16139          */
16140     simpleAccess: function(obj, subsc) {
16141         return obj[subsc];
16142     },
16143
16144         /**
16145          * @ignore
16146          */
16147     getJsonAccessor: function(){
16148         var re = /[\[\.]/;
16149         return function(expr) {
16150             try {
16151                 return(re.test(expr))
16152                     ? new Function("obj", "return obj." + expr)
16153                     : function(obj){
16154                         return obj[expr];
16155                     };
16156             } catch(e){}
16157             return Roo.emptyFn;
16158         };
16159     }(),
16160
16161     /**
16162      * Create a data block containing Roo.data.Records from an XML document.
16163      * @param {Object} o An object which contains an Array of row objects in the property specified
16164      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16165      * which contains the total size of the dataset.
16166      * @return {Object} data A data block which is used by an Roo.data.Store object as
16167      * a cache of Roo.data.Records.
16168      */
16169     readRecords : function(o){
16170         /**
16171          * After any data loads, the raw JSON data is available for further custom processing.
16172          * @type Object
16173          */
16174         this.o = o;
16175         var s = this.meta, Record = this.recordType,
16176             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16177
16178 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16179         if (!this.ef) {
16180             if(s.totalProperty) {
16181                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16182                 }
16183                 if(s.successProperty) {
16184                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16185                 }
16186                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16187                 if (s.id) {
16188                         var g = this.getJsonAccessor(s.id);
16189                         this.getId = function(rec) {
16190                                 var r = g(rec);  
16191                                 return (r === undefined || r === "") ? null : r;
16192                         };
16193                 } else {
16194                         this.getId = function(){return null;};
16195                 }
16196             this.ef = [];
16197             for(var jj = 0; jj < fl; jj++){
16198                 f = fi[jj];
16199                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16200                 this.ef[jj] = this.getJsonAccessor(map);
16201             }
16202         }
16203
16204         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16205         if(s.totalProperty){
16206             var vt = parseInt(this.getTotal(o), 10);
16207             if(!isNaN(vt)){
16208                 totalRecords = vt;
16209             }
16210         }
16211         if(s.successProperty){
16212             var vs = this.getSuccess(o);
16213             if(vs === false || vs === 'false'){
16214                 success = false;
16215             }
16216         }
16217         var records = [];
16218         for(var i = 0; i < c; i++){
16219                 var n = root[i];
16220             var values = {};
16221             var id = this.getId(n);
16222             for(var j = 0; j < fl; j++){
16223                 f = fi[j];
16224             var v = this.ef[j](n);
16225             if (!f.convert) {
16226                 Roo.log('missing convert for ' + f.name);
16227                 Roo.log(f);
16228                 continue;
16229             }
16230             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16231             }
16232             var record = new Record(values, id);
16233             record.json = n;
16234             records[i] = record;
16235         }
16236         return {
16237             raw : o,
16238             success : success,
16239             records : records,
16240             totalRecords : totalRecords
16241         };
16242     },
16243     // used when loading children.. @see loadDataFromChildren
16244     toLoadData: function(rec)
16245     {
16246         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16247         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16248         return { data : data, total : data.length };
16249         
16250     }
16251 });/*
16252  * Based on:
16253  * Ext JS Library 1.1.1
16254  * Copyright(c) 2006-2007, Ext JS, LLC.
16255  *
16256  * Originally Released Under LGPL - original licence link has changed is not relivant.
16257  *
16258  * Fork - LGPL
16259  * <script type="text/javascript">
16260  */
16261
16262 /**
16263  * @class Roo.data.ArrayReader
16264  * @extends Roo.data.DataReader
16265  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16266  * Each element of that Array represents a row of data fields. The
16267  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16268  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16269  * <p>
16270  * Example code:.
16271  * <pre><code>
16272 var RecordDef = Roo.data.Record.create([
16273     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16274     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16275 ]);
16276 var myReader = new Roo.data.ArrayReader({
16277     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16278 }, RecordDef);
16279 </code></pre>
16280  * <p>
16281  * This would consume an Array like this:
16282  * <pre><code>
16283 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16284   </code></pre>
16285  
16286  * @constructor
16287  * Create a new JsonReader
16288  * @param {Object} meta Metadata configuration options.
16289  * @param {Object|Array} recordType Either an Array of field definition objects
16290  * 
16291  * @cfg {Array} fields Array of field definition objects
16292  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16293  * as specified to {@link Roo.data.Record#create},
16294  * or an {@link Roo.data.Record} object
16295  *
16296  * 
16297  * created using {@link Roo.data.Record#create}.
16298  */
16299 Roo.data.ArrayReader = function(meta, recordType)
16300 {    
16301     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16302 };
16303
16304 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16305     
16306       /**
16307      * Create a data block containing Roo.data.Records from an XML document.
16308      * @param {Object} o An Array of row objects which represents the dataset.
16309      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16310      * a cache of Roo.data.Records.
16311      */
16312     readRecords : function(o)
16313     {
16314         var sid = this.meta ? this.meta.id : null;
16315         var recordType = this.recordType, fields = recordType.prototype.fields;
16316         var records = [];
16317         var root = o;
16318         for(var i = 0; i < root.length; i++){
16319             var n = root[i];
16320             var values = {};
16321             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16322             for(var j = 0, jlen = fields.length; j < jlen; j++){
16323                 var f = fields.items[j];
16324                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16325                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16326                 v = f.convert(v);
16327                 values[f.name] = v;
16328             }
16329             var record = new recordType(values, id);
16330             record.json = n;
16331             records[records.length] = record;
16332         }
16333         return {
16334             records : records,
16335             totalRecords : records.length
16336         };
16337     },
16338     // used when loading children.. @see loadDataFromChildren
16339     toLoadData: function(rec)
16340     {
16341         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16342         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16343         
16344     }
16345     
16346     
16347 });/*
16348  * - LGPL
16349  * * 
16350  */
16351
16352 /**
16353  * @class Roo.bootstrap.ComboBox
16354  * @extends Roo.bootstrap.TriggerField
16355  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16356  * @cfg {Boolean} append (true|false) default false
16357  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16358  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16359  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16360  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16361  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16362  * @cfg {Boolean} animate default true
16363  * @cfg {Boolean} emptyResultText only for touch device
16364  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16365  * @cfg {String} emptyTitle default ''
16366  * @cfg {Number} width fixed with? experimental
16367  * @constructor
16368  * Create a new ComboBox.
16369  * @param {Object} config Configuration options
16370  */
16371 Roo.bootstrap.ComboBox = function(config){
16372     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16373     this.addEvents({
16374         /**
16375          * @event expand
16376          * Fires when the dropdown list is expanded
16377         * @param {Roo.bootstrap.ComboBox} combo This combo box
16378         */
16379         'expand' : true,
16380         /**
16381          * @event collapse
16382          * Fires when the dropdown list is collapsed
16383         * @param {Roo.bootstrap.ComboBox} combo This combo box
16384         */
16385         'collapse' : true,
16386         /**
16387          * @event beforeselect
16388          * Fires before a list item is selected. Return false to cancel the selection.
16389         * @param {Roo.bootstrap.ComboBox} combo This combo box
16390         * @param {Roo.data.Record} record The data record returned from the underlying store
16391         * @param {Number} index The index of the selected item in the dropdown list
16392         */
16393         'beforeselect' : true,
16394         /**
16395          * @event select
16396          * Fires when a list item is selected
16397         * @param {Roo.bootstrap.ComboBox} combo This combo box
16398         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16399         * @param {Number} index The index of the selected item in the dropdown list
16400         */
16401         'select' : true,
16402         /**
16403          * @event beforequery
16404          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16405          * The event object passed has these properties:
16406         * @param {Roo.bootstrap.ComboBox} combo This combo box
16407         * @param {String} query The query
16408         * @param {Boolean} forceAll true to force "all" query
16409         * @param {Boolean} cancel true to cancel the query
16410         * @param {Object} e The query event object
16411         */
16412         'beforequery': true,
16413          /**
16414          * @event add
16415          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16416         * @param {Roo.bootstrap.ComboBox} combo This combo box
16417         */
16418         'add' : true,
16419         /**
16420          * @event edit
16421          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16424         */
16425         'edit' : true,
16426         /**
16427          * @event remove
16428          * Fires when the remove value from the combobox array
16429         * @param {Roo.bootstrap.ComboBox} combo This combo box
16430         */
16431         'remove' : true,
16432         /**
16433          * @event afterremove
16434          * Fires when the remove value from the combobox array
16435         * @param {Roo.bootstrap.ComboBox} combo This combo box
16436         */
16437         'afterremove' : true,
16438         /**
16439          * @event specialfilter
16440          * Fires when specialfilter
16441             * @param {Roo.bootstrap.ComboBox} combo This combo box
16442             */
16443         'specialfilter' : true,
16444         /**
16445          * @event tick
16446          * Fires when tick the element
16447             * @param {Roo.bootstrap.ComboBox} combo This combo box
16448             */
16449         'tick' : true,
16450         /**
16451          * @event touchviewdisplay
16452          * Fires when touch view require special display (default is using displayField)
16453             * @param {Roo.bootstrap.ComboBox} combo This combo box
16454             * @param {Object} cfg set html .
16455             */
16456         'touchviewdisplay' : true
16457         
16458     });
16459     
16460     this.item = [];
16461     this.tickItems = [];
16462     
16463     this.selectedIndex = -1;
16464     if(this.mode == 'local'){
16465         if(config.queryDelay === undefined){
16466             this.queryDelay = 10;
16467         }
16468         if(config.minChars === undefined){
16469             this.minChars = 0;
16470         }
16471     }
16472 };
16473
16474 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16475      
16476     /**
16477      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16478      * rendering into an Roo.Editor, defaults to false)
16479      */
16480     /**
16481      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16482      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16483      */
16484     /**
16485      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16486      */
16487     /**
16488      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16489      * the dropdown list (defaults to undefined, with no header element)
16490      */
16491
16492      /**
16493      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16494      */
16495      
16496      /**
16497      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16498      */
16499     listWidth: undefined,
16500     /**
16501      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16502      * mode = 'remote' or 'text' if mode = 'local')
16503      */
16504     displayField: undefined,
16505     
16506     /**
16507      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16508      * mode = 'remote' or 'value' if mode = 'local'). 
16509      * Note: use of a valueField requires the user make a selection
16510      * in order for a value to be mapped.
16511      */
16512     valueField: undefined,
16513     /**
16514      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16515      */
16516     modalTitle : '',
16517     
16518     /**
16519      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16520      * field's data value (defaults to the underlying DOM element's name)
16521      */
16522     hiddenName: undefined,
16523     /**
16524      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16525      */
16526     listClass: '',
16527     /**
16528      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16529      */
16530     selectedClass: 'active',
16531     
16532     /**
16533      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16534      */
16535     shadow:'sides',
16536     /**
16537      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16538      * anchor positions (defaults to 'tl-bl')
16539      */
16540     listAlign: 'tl-bl?',
16541     /**
16542      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16543      */
16544     maxHeight: 300,
16545     /**
16546      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16547      * query specified by the allQuery config option (defaults to 'query')
16548      */
16549     triggerAction: 'query',
16550     /**
16551      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16552      * (defaults to 4, does not apply if editable = false)
16553      */
16554     minChars : 4,
16555     /**
16556      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16557      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16558      */
16559     typeAhead: false,
16560     /**
16561      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16562      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16563      */
16564     queryDelay: 500,
16565     /**
16566      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16567      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16568      */
16569     pageSize: 0,
16570     /**
16571      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16572      * when editable = true (defaults to false)
16573      */
16574     selectOnFocus:false,
16575     /**
16576      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16577      */
16578     queryParam: 'query',
16579     /**
16580      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16581      * when mode = 'remote' (defaults to 'Loading...')
16582      */
16583     loadingText: 'Loading...',
16584     /**
16585      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16586      */
16587     resizable: false,
16588     /**
16589      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16590      */
16591     handleHeight : 8,
16592     /**
16593      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16594      * traditional select (defaults to true)
16595      */
16596     editable: true,
16597     /**
16598      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16599      */
16600     allQuery: '',
16601     /**
16602      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16603      */
16604     mode: 'remote',
16605     /**
16606      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16607      * listWidth has a higher value)
16608      */
16609     minListWidth : 70,
16610     /**
16611      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16612      * allow the user to set arbitrary text into the field (defaults to false)
16613      */
16614     forceSelection:false,
16615     /**
16616      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16617      * if typeAhead = true (defaults to 250)
16618      */
16619     typeAheadDelay : 250,
16620     /**
16621      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16622      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16623      */
16624     valueNotFoundText : undefined,
16625     /**
16626      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16627      */
16628     blockFocus : false,
16629     
16630     /**
16631      * @cfg {Boolean} disableClear Disable showing of clear button.
16632      */
16633     disableClear : false,
16634     /**
16635      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16636      */
16637     alwaysQuery : false,
16638     
16639     /**
16640      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16641      */
16642     multiple : false,
16643     
16644     /**
16645      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16646      */
16647     invalidClass : "has-warning",
16648     
16649     /**
16650      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16651      */
16652     validClass : "has-success",
16653     
16654     /**
16655      * @cfg {Boolean} specialFilter (true|false) special filter default false
16656      */
16657     specialFilter : false,
16658     
16659     /**
16660      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16661      */
16662     mobileTouchView : true,
16663     
16664     /**
16665      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16666      */
16667     useNativeIOS : false,
16668     
16669     /**
16670      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16671      */
16672     mobile_restrict_height : false,
16673     
16674     ios_options : false,
16675     
16676     //private
16677     addicon : false,
16678     editicon: false,
16679     
16680     page: 0,
16681     hasQuery: false,
16682     append: false,
16683     loadNext: false,
16684     autoFocus : true,
16685     tickable : false,
16686     btnPosition : 'right',
16687     triggerList : true,
16688     showToggleBtn : true,
16689     animate : true,
16690     emptyResultText: 'Empty',
16691     triggerText : 'Select',
16692     emptyTitle : '',
16693     width : false,
16694     
16695     // element that contains real text value.. (when hidden is used..)
16696     
16697     getAutoCreate : function()
16698     {   
16699         var cfg = false;
16700         //render
16701         /*
16702          * Render classic select for iso
16703          */
16704         
16705         if(Roo.isIOS && this.useNativeIOS){
16706             cfg = this.getAutoCreateNativeIOS();
16707             return cfg;
16708         }
16709         
16710         /*
16711          * Touch Devices
16712          */
16713         
16714         if(Roo.isTouch && this.mobileTouchView){
16715             cfg = this.getAutoCreateTouchView();
16716             return cfg;;
16717         }
16718         
16719         /*
16720          *  Normal ComboBox
16721          */
16722         if(!this.tickable){
16723             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16724             return cfg;
16725         }
16726         
16727         /*
16728          *  ComboBox with tickable selections
16729          */
16730              
16731         var align = this.labelAlign || this.parentLabelAlign();
16732         
16733         cfg = {
16734             cls : 'form-group roo-combobox-tickable' //input-group
16735         };
16736         
16737         var btn_text_select = '';
16738         var btn_text_done = '';
16739         var btn_text_cancel = '';
16740         
16741         if (this.btn_text_show) {
16742             btn_text_select = 'Select';
16743             btn_text_done = 'Done';
16744             btn_text_cancel = 'Cancel'; 
16745         }
16746         
16747         var buttons = {
16748             tag : 'div',
16749             cls : 'tickable-buttons',
16750             cn : [
16751                 {
16752                     tag : 'button',
16753                     type : 'button',
16754                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16755                     //html : this.triggerText
16756                     html: btn_text_select
16757                 },
16758                 {
16759                     tag : 'button',
16760                     type : 'button',
16761                     name : 'ok',
16762                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16763                     //html : 'Done'
16764                     html: btn_text_done
16765                 },
16766                 {
16767                     tag : 'button',
16768                     type : 'button',
16769                     name : 'cancel',
16770                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16771                     //html : 'Cancel'
16772                     html: btn_text_cancel
16773                 }
16774             ]
16775         };
16776         
16777         if(this.editable){
16778             buttons.cn.unshift({
16779                 tag: 'input',
16780                 cls: 'roo-select2-search-field-input'
16781             });
16782         }
16783         
16784         var _this = this;
16785         
16786         Roo.each(buttons.cn, function(c){
16787             if (_this.size) {
16788                 c.cls += ' btn-' + _this.size;
16789             }
16790
16791             if (_this.disabled) {
16792                 c.disabled = true;
16793             }
16794         });
16795         
16796         var box = {
16797             tag: 'div',
16798             style : 'display: contents',
16799             cn: [
16800                 {
16801                     tag: 'input',
16802                     type : 'hidden',
16803                     cls: 'form-hidden-field'
16804                 },
16805                 {
16806                     tag: 'ul',
16807                     cls: 'roo-select2-choices',
16808                     cn:[
16809                         {
16810                             tag: 'li',
16811                             cls: 'roo-select2-search-field',
16812                             cn: [
16813                                 buttons
16814                             ]
16815                         }
16816                     ]
16817                 }
16818             ]
16819         };
16820         
16821         var combobox = {
16822             cls: 'roo-select2-container input-group roo-select2-container-multi',
16823             cn: [
16824                 
16825                 box
16826 //                {
16827 //                    tag: 'ul',
16828 //                    cls: 'typeahead typeahead-long dropdown-menu',
16829 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16830 //                }
16831             ]
16832         };
16833         
16834         if(this.hasFeedback && !this.allowBlank){
16835             
16836             var feedback = {
16837                 tag: 'span',
16838                 cls: 'glyphicon form-control-feedback'
16839             };
16840
16841             combobox.cn.push(feedback);
16842         }
16843         
16844         
16845         
16846         var indicator = {
16847             tag : 'i',
16848             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16849             tooltip : 'This field is required'
16850         };
16851         if (Roo.bootstrap.version == 4) {
16852             indicator = {
16853                 tag : 'i',
16854                 style : 'display:none'
16855             };
16856         }
16857         if (align ==='left' && this.fieldLabel.length) {
16858             
16859             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16860             
16861             cfg.cn = [
16862                 indicator,
16863                 {
16864                     tag: 'label',
16865                     'for' :  id,
16866                     cls : 'control-label col-form-label',
16867                     html : this.fieldLabel
16868
16869                 },
16870                 {
16871                     cls : "", 
16872                     cn: [
16873                         combobox
16874                     ]
16875                 }
16876
16877             ];
16878             
16879             var labelCfg = cfg.cn[1];
16880             var contentCfg = cfg.cn[2];
16881             
16882
16883             if(this.indicatorpos == 'right'){
16884                 
16885                 cfg.cn = [
16886                     {
16887                         tag: 'label',
16888                         'for' :  id,
16889                         cls : 'control-label col-form-label',
16890                         cn : [
16891                             {
16892                                 tag : 'span',
16893                                 html : this.fieldLabel
16894                             },
16895                             indicator
16896                         ]
16897                     },
16898                     {
16899                         cls : "",
16900                         cn: [
16901                             combobox
16902                         ]
16903                     }
16904
16905                 ];
16906                 
16907                 
16908                 
16909                 labelCfg = cfg.cn[0];
16910                 contentCfg = cfg.cn[1];
16911             
16912             }
16913             
16914             if(this.labelWidth > 12){
16915                 labelCfg.style = "width: " + this.labelWidth + 'px';
16916             }
16917             if(this.width * 1 > 0){
16918                 contentCfg.style = "width: " + this.width + 'px';
16919             }
16920             if(this.labelWidth < 13 && this.labelmd == 0){
16921                 this.labelmd = this.labelWidth;
16922             }
16923             
16924             if(this.labellg > 0){
16925                 labelCfg.cls += ' col-lg-' + this.labellg;
16926                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16927             }
16928             
16929             if(this.labelmd > 0){
16930                 labelCfg.cls += ' col-md-' + this.labelmd;
16931                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16932             }
16933             
16934             if(this.labelsm > 0){
16935                 labelCfg.cls += ' col-sm-' + this.labelsm;
16936                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16937             }
16938             
16939             if(this.labelxs > 0){
16940                 labelCfg.cls += ' col-xs-' + this.labelxs;
16941                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16942             }
16943                 
16944                 
16945         } else if ( this.fieldLabel.length) {
16946 //                Roo.log(" label");
16947                  cfg.cn = [
16948                    indicator,
16949                     {
16950                         tag: 'label',
16951                         //cls : 'input-group-addon',
16952                         html : this.fieldLabel
16953                     },
16954                     combobox
16955                 ];
16956                 
16957                 if(this.indicatorpos == 'right'){
16958                     cfg.cn = [
16959                         {
16960                             tag: 'label',
16961                             //cls : 'input-group-addon',
16962                             html : this.fieldLabel
16963                         },
16964                         indicator,
16965                         combobox
16966                     ];
16967                     
16968                 }
16969
16970         } else {
16971             
16972 //                Roo.log(" no label && no align");
16973                 cfg = combobox
16974                      
16975                 
16976         }
16977          
16978         var settings=this;
16979         ['xs','sm','md','lg'].map(function(size){
16980             if (settings[size]) {
16981                 cfg.cls += ' col-' + size + '-' + settings[size];
16982             }
16983         });
16984         
16985         return cfg;
16986         
16987     },
16988     
16989     _initEventsCalled : false,
16990     
16991     // private
16992     initEvents: function()
16993     {   
16994         if (this._initEventsCalled) { // as we call render... prevent looping...
16995             return;
16996         }
16997         this._initEventsCalled = true;
16998         
16999         if (!this.store) {
17000             throw "can not find store for combo";
17001         }
17002         
17003         this.indicator = this.indicatorEl();
17004         
17005         this.store = Roo.factory(this.store, Roo.data);
17006         this.store.parent = this;
17007         
17008         // if we are building from html. then this element is so complex, that we can not really
17009         // use the rendered HTML.
17010         // so we have to trash and replace the previous code.
17011         if (Roo.XComponent.build_from_html) {
17012             // remove this element....
17013             var e = this.el.dom, k=0;
17014             while (e ) { e = e.previousSibling;  ++k;}
17015
17016             this.el.remove();
17017             
17018             this.el=false;
17019             this.rendered = false;
17020             
17021             this.render(this.parent().getChildContainer(true), k);
17022         }
17023         
17024         if(Roo.isIOS && this.useNativeIOS){
17025             this.initIOSView();
17026             return;
17027         }
17028         
17029         /*
17030          * Touch Devices
17031          */
17032         
17033         if(Roo.isTouch && this.mobileTouchView){
17034             this.initTouchView();
17035             return;
17036         }
17037         
17038         if(this.tickable){
17039             this.initTickableEvents();
17040             return;
17041         }
17042         
17043         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17044         
17045         if(this.hiddenName){
17046             
17047             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17048             
17049             this.hiddenField.dom.value =
17050                 this.hiddenValue !== undefined ? this.hiddenValue :
17051                 this.value !== undefined ? this.value : '';
17052
17053             // prevent input submission
17054             this.el.dom.removeAttribute('name');
17055             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17056              
17057              
17058         }
17059         //if(Roo.isGecko){
17060         //    this.el.dom.setAttribute('autocomplete', 'off');
17061         //}
17062         
17063         var cls = 'x-combo-list';
17064         
17065         //this.list = new Roo.Layer({
17066         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17067         //});
17068         
17069         var _this = this;
17070         
17071         (function(){
17072             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17073             _this.list.setWidth(lw);
17074         }).defer(100);
17075         
17076         this.list.on('mouseover', this.onViewOver, this);
17077         this.list.on('mousemove', this.onViewMove, this);
17078         this.list.on('scroll', this.onViewScroll, this);
17079         
17080         /*
17081         this.list.swallowEvent('mousewheel');
17082         this.assetHeight = 0;
17083
17084         if(this.title){
17085             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17086             this.assetHeight += this.header.getHeight();
17087         }
17088
17089         this.innerList = this.list.createChild({cls:cls+'-inner'});
17090         this.innerList.on('mouseover', this.onViewOver, this);
17091         this.innerList.on('mousemove', this.onViewMove, this);
17092         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17093         
17094         if(this.allowBlank && !this.pageSize && !this.disableClear){
17095             this.footer = this.list.createChild({cls:cls+'-ft'});
17096             this.pageTb = new Roo.Toolbar(this.footer);
17097            
17098         }
17099         if(this.pageSize){
17100             this.footer = this.list.createChild({cls:cls+'-ft'});
17101             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17102                     {pageSize: this.pageSize});
17103             
17104         }
17105         
17106         if (this.pageTb && this.allowBlank && !this.disableClear) {
17107             var _this = this;
17108             this.pageTb.add(new Roo.Toolbar.Fill(), {
17109                 cls: 'x-btn-icon x-btn-clear',
17110                 text: '&#160;',
17111                 handler: function()
17112                 {
17113                     _this.collapse();
17114                     _this.clearValue();
17115                     _this.onSelect(false, -1);
17116                 }
17117             });
17118         }
17119         if (this.footer) {
17120             this.assetHeight += this.footer.getHeight();
17121         }
17122         */
17123             
17124         if(!this.tpl){
17125             this.tpl = Roo.bootstrap.version == 4 ?
17126                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17127                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17128         }
17129
17130         this.view = new Roo.View(this.list, this.tpl, {
17131             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17132         });
17133         //this.view.wrapEl.setDisplayed(false);
17134         this.view.on('click', this.onViewClick, this);
17135         
17136         
17137         this.store.on('beforeload', this.onBeforeLoad, this);
17138         this.store.on('load', this.onLoad, this);
17139         this.store.on('loadexception', this.onLoadException, this);
17140         /*
17141         if(this.resizable){
17142             this.resizer = new Roo.Resizable(this.list,  {
17143                pinned:true, handles:'se'
17144             });
17145             this.resizer.on('resize', function(r, w, h){
17146                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17147                 this.listWidth = w;
17148                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17149                 this.restrictHeight();
17150             }, this);
17151             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17152         }
17153         */
17154         if(!this.editable){
17155             this.editable = true;
17156             this.setEditable(false);
17157         }
17158         
17159         /*
17160         
17161         if (typeof(this.events.add.listeners) != 'undefined') {
17162             
17163             this.addicon = this.wrap.createChild(
17164                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17165        
17166             this.addicon.on('click', function(e) {
17167                 this.fireEvent('add', this);
17168             }, this);
17169         }
17170         if (typeof(this.events.edit.listeners) != 'undefined') {
17171             
17172             this.editicon = this.wrap.createChild(
17173                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17174             if (this.addicon) {
17175                 this.editicon.setStyle('margin-left', '40px');
17176             }
17177             this.editicon.on('click', function(e) {
17178                 
17179                 // we fire even  if inothing is selected..
17180                 this.fireEvent('edit', this, this.lastData );
17181                 
17182             }, this);
17183         }
17184         */
17185         
17186         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17187             "up" : function(e){
17188                 this.inKeyMode = true;
17189                 this.selectPrev();
17190             },
17191
17192             "down" : function(e){
17193                 if(!this.isExpanded()){
17194                     this.onTriggerClick();
17195                 }else{
17196                     this.inKeyMode = true;
17197                     this.selectNext();
17198                 }
17199             },
17200
17201             "enter" : function(e){
17202 //                this.onViewClick();
17203                 //return true;
17204                 this.collapse();
17205                 
17206                 if(this.fireEvent("specialkey", this, e)){
17207                     this.onViewClick(false);
17208                 }
17209                 
17210                 return true;
17211             },
17212
17213             "esc" : function(e){
17214                 this.collapse();
17215             },
17216
17217             "tab" : function(e){
17218                 this.collapse();
17219                 
17220                 if(this.fireEvent("specialkey", this, e)){
17221                     this.onViewClick(false);
17222                 }
17223                 
17224                 return true;
17225             },
17226
17227             scope : this,
17228
17229             doRelay : function(foo, bar, hname){
17230                 if(hname == 'down' || this.scope.isExpanded()){
17231                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17232                 }
17233                 return true;
17234             },
17235
17236             forceKeyDown: true
17237         });
17238         
17239         
17240         this.queryDelay = Math.max(this.queryDelay || 10,
17241                 this.mode == 'local' ? 10 : 250);
17242         
17243         
17244         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17245         
17246         if(this.typeAhead){
17247             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17248         }
17249         if(this.editable !== false){
17250             this.inputEl().on("keyup", this.onKeyUp, this);
17251         }
17252         if(this.forceSelection){
17253             this.inputEl().on('blur', this.doForce, this);
17254         }
17255         
17256         if(this.multiple){
17257             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17258             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17259         }
17260     },
17261     
17262     initTickableEvents: function()
17263     {   
17264         this.createList();
17265         
17266         if(this.hiddenName){
17267             
17268             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17269             
17270             this.hiddenField.dom.value =
17271                 this.hiddenValue !== undefined ? this.hiddenValue :
17272                 this.value !== undefined ? this.value : '';
17273
17274             // prevent input submission
17275             this.el.dom.removeAttribute('name');
17276             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17277              
17278              
17279         }
17280         
17281 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17282         
17283         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17284         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17285         if(this.triggerList){
17286             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17287         }
17288          
17289         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17290         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17291         
17292         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17293         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17294         
17295         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17296         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17297         
17298         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17299         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17300         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17301         
17302         this.okBtn.hide();
17303         this.cancelBtn.hide();
17304         
17305         var _this = this;
17306         
17307         (function(){
17308             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17309             _this.list.setWidth(lw);
17310         }).defer(100);
17311         
17312         this.list.on('mouseover', this.onViewOver, this);
17313         this.list.on('mousemove', this.onViewMove, this);
17314         
17315         this.list.on('scroll', this.onViewScroll, this);
17316         
17317         if(!this.tpl){
17318             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17319                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17320         }
17321
17322         this.view = new Roo.View(this.list, this.tpl, {
17323             singleSelect:true,
17324             tickable:true,
17325             parent:this,
17326             store: this.store,
17327             selectedClass: this.selectedClass
17328         });
17329         
17330         //this.view.wrapEl.setDisplayed(false);
17331         this.view.on('click', this.onViewClick, this);
17332         
17333         
17334         
17335         this.store.on('beforeload', this.onBeforeLoad, this);
17336         this.store.on('load', this.onLoad, this);
17337         this.store.on('loadexception', this.onLoadException, this);
17338         
17339         if(this.editable){
17340             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17341                 "up" : function(e){
17342                     this.inKeyMode = true;
17343                     this.selectPrev();
17344                 },
17345
17346                 "down" : function(e){
17347                     this.inKeyMode = true;
17348                     this.selectNext();
17349                 },
17350
17351                 "enter" : function(e){
17352                     if(this.fireEvent("specialkey", this, e)){
17353                         this.onViewClick(false);
17354                     }
17355                     
17356                     return true;
17357                 },
17358
17359                 "esc" : function(e){
17360                     this.onTickableFooterButtonClick(e, false, false);
17361                 },
17362
17363                 "tab" : function(e){
17364                     this.fireEvent("specialkey", this, e);
17365                     
17366                     this.onTickableFooterButtonClick(e, false, false);
17367                     
17368                     return true;
17369                 },
17370
17371                 scope : this,
17372
17373                 doRelay : function(e, fn, key){
17374                     if(this.scope.isExpanded()){
17375                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17376                     }
17377                     return true;
17378                 },
17379
17380                 forceKeyDown: true
17381             });
17382         }
17383         
17384         this.queryDelay = Math.max(this.queryDelay || 10,
17385                 this.mode == 'local' ? 10 : 250);
17386         
17387         
17388         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17389         
17390         if(this.typeAhead){
17391             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17392         }
17393         
17394         if(this.editable !== false){
17395             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17396         }
17397         
17398         this.indicator = this.indicatorEl();
17399         
17400         if(this.indicator){
17401             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17402             this.indicator.hide();
17403         }
17404         
17405     },
17406
17407     onDestroy : function(){
17408         if(this.view){
17409             this.view.setStore(null);
17410             this.view.el.removeAllListeners();
17411             this.view.el.remove();
17412             this.view.purgeListeners();
17413         }
17414         if(this.list){
17415             this.list.dom.innerHTML  = '';
17416         }
17417         
17418         if(this.store){
17419             this.store.un('beforeload', this.onBeforeLoad, this);
17420             this.store.un('load', this.onLoad, this);
17421             this.store.un('loadexception', this.onLoadException, this);
17422         }
17423         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17424     },
17425
17426     // private
17427     fireKey : function(e){
17428         if(e.isNavKeyPress() && !this.list.isVisible()){
17429             this.fireEvent("specialkey", this, e);
17430         }
17431     },
17432
17433     // private
17434     onResize: function(w, h)
17435     {
17436         
17437         
17438 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17439 //        
17440 //        if(typeof w != 'number'){
17441 //            // we do not handle it!?!?
17442 //            return;
17443 //        }
17444 //        var tw = this.trigger.getWidth();
17445 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17446 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17447 //        var x = w - tw;
17448 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17449 //            
17450 //        //this.trigger.setStyle('left', x+'px');
17451 //        
17452 //        if(this.list && this.listWidth === undefined){
17453 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17454 //            this.list.setWidth(lw);
17455 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17456 //        }
17457         
17458     
17459         
17460     },
17461
17462     /**
17463      * Allow or prevent the user from directly editing the field text.  If false is passed,
17464      * the user will only be able to select from the items defined in the dropdown list.  This method
17465      * is the runtime equivalent of setting the 'editable' config option at config time.
17466      * @param {Boolean} value True to allow the user to directly edit the field text
17467      */
17468     setEditable : function(value){
17469         if(value == this.editable){
17470             return;
17471         }
17472         this.editable = value;
17473         if(!value){
17474             this.inputEl().dom.setAttribute('readOnly', true);
17475             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17476             this.inputEl().addClass('x-combo-noedit');
17477         }else{
17478             this.inputEl().dom.removeAttribute('readOnly');
17479             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17480             this.inputEl().removeClass('x-combo-noedit');
17481         }
17482     },
17483
17484     // private
17485     
17486     onBeforeLoad : function(combo,opts){
17487         if(!this.hasFocus){
17488             return;
17489         }
17490          if (!opts.add) {
17491             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17492          }
17493         this.restrictHeight();
17494         this.selectedIndex = -1;
17495     },
17496
17497     // private
17498     onLoad : function(){
17499         
17500         this.hasQuery = false;
17501         
17502         if(!this.hasFocus){
17503             return;
17504         }
17505         
17506         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17507             this.loading.hide();
17508         }
17509         
17510         if(this.store.getCount() > 0){
17511             
17512             this.expand();
17513             this.restrictHeight();
17514             if(this.lastQuery == this.allQuery){
17515                 if(this.editable && !this.tickable){
17516                     this.inputEl().dom.select();
17517                 }
17518                 
17519                 if(
17520                     !this.selectByValue(this.value, true) &&
17521                     this.autoFocus && 
17522                     (
17523                         !this.store.lastOptions ||
17524                         typeof(this.store.lastOptions.add) == 'undefined' || 
17525                         this.store.lastOptions.add != true
17526                     )
17527                 ){
17528                     this.select(0, true);
17529                 }
17530             }else{
17531                 if(this.autoFocus){
17532                     this.selectNext();
17533                 }
17534                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17535                     this.taTask.delay(this.typeAheadDelay);
17536                 }
17537             }
17538         }else{
17539             this.onEmptyResults();
17540         }
17541         
17542         //this.el.focus();
17543     },
17544     // private
17545     onLoadException : function()
17546     {
17547         this.hasQuery = false;
17548         
17549         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17550             this.loading.hide();
17551         }
17552         
17553         if(this.tickable && this.editable){
17554             return;
17555         }
17556         
17557         this.collapse();
17558         // only causes errors at present
17559         //Roo.log(this.store.reader.jsonData);
17560         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17561             // fixme
17562             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17563         //}
17564         
17565         
17566     },
17567     // private
17568     onTypeAhead : function(){
17569         if(this.store.getCount() > 0){
17570             var r = this.store.getAt(0);
17571             var newValue = r.data[this.displayField];
17572             var len = newValue.length;
17573             var selStart = this.getRawValue().length;
17574             
17575             if(selStart != len){
17576                 this.setRawValue(newValue);
17577                 this.selectText(selStart, newValue.length);
17578             }
17579         }
17580     },
17581
17582     // private
17583     onSelect : function(record, index){
17584         
17585         if(this.fireEvent('beforeselect', this, record, index) !== false){
17586         
17587             this.setFromData(index > -1 ? record.data : false);
17588             
17589             this.collapse();
17590             this.fireEvent('select', this, record, index);
17591         }
17592     },
17593
17594     /**
17595      * Returns the currently selected field value or empty string if no value is set.
17596      * @return {String} value The selected value
17597      */
17598     getValue : function()
17599     {
17600         if(Roo.isIOS && this.useNativeIOS){
17601             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17602         }
17603         
17604         if(this.multiple){
17605             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17606         }
17607         
17608         if(this.valueField){
17609             return typeof this.value != 'undefined' ? this.value : '';
17610         }else{
17611             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17612         }
17613     },
17614     
17615     getRawValue : function()
17616     {
17617         if(Roo.isIOS && this.useNativeIOS){
17618             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17619         }
17620         
17621         var v = this.inputEl().getValue();
17622         
17623         return v;
17624     },
17625
17626     /**
17627      * Clears any text/value currently set in the field
17628      */
17629     clearValue : function(){
17630         
17631         if(this.hiddenField){
17632             this.hiddenField.dom.value = '';
17633         }
17634         this.value = '';
17635         this.setRawValue('');
17636         this.lastSelectionText = '';
17637         this.lastData = false;
17638         
17639         var close = this.closeTriggerEl();
17640         
17641         if(close){
17642             close.hide();
17643         }
17644         
17645         this.validate();
17646         
17647     },
17648
17649     /**
17650      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17651      * will be displayed in the field.  If the value does not match the data value of an existing item,
17652      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17653      * Otherwise the field will be blank (although the value will still be set).
17654      * @param {String} value The value to match
17655      */
17656     setValue : function(v)
17657     {
17658         if(Roo.isIOS && this.useNativeIOS){
17659             this.setIOSValue(v);
17660             return;
17661         }
17662         
17663         if(this.multiple){
17664             this.syncValue();
17665             return;
17666         }
17667         
17668         var text = v;
17669         if(this.valueField){
17670             var r = this.findRecord(this.valueField, v);
17671             if(r){
17672                 text = r.data[this.displayField];
17673             }else if(this.valueNotFoundText !== undefined){
17674                 text = this.valueNotFoundText;
17675             }
17676         }
17677         this.lastSelectionText = text;
17678         if(this.hiddenField){
17679             this.hiddenField.dom.value = v;
17680         }
17681         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17682         this.value = v;
17683         
17684         var close = this.closeTriggerEl();
17685         
17686         if(close){
17687             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17688         }
17689         
17690         this.validate();
17691     },
17692     /**
17693      * @property {Object} the last set data for the element
17694      */
17695     
17696     lastData : false,
17697     /**
17698      * Sets the value of the field based on a object which is related to the record format for the store.
17699      * @param {Object} value the value to set as. or false on reset?
17700      */
17701     setFromData : function(o){
17702         
17703         if(this.multiple){
17704             this.addItem(o);
17705             return;
17706         }
17707             
17708         var dv = ''; // display value
17709         var vv = ''; // value value..
17710         this.lastData = o;
17711         if (this.displayField) {
17712             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17713         } else {
17714             // this is an error condition!!!
17715             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17716         }
17717         
17718         if(this.valueField){
17719             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17720         }
17721         
17722         var close = this.closeTriggerEl();
17723         
17724         if(close){
17725             if(dv.length || vv * 1 > 0){
17726                 close.show() ;
17727                 this.blockFocus=true;
17728             } else {
17729                 close.hide();
17730             }             
17731         }
17732         
17733         if(this.hiddenField){
17734             this.hiddenField.dom.value = vv;
17735             
17736             this.lastSelectionText = dv;
17737             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17738             this.value = vv;
17739             return;
17740         }
17741         // no hidden field.. - we store the value in 'value', but still display
17742         // display field!!!!
17743         this.lastSelectionText = dv;
17744         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17745         this.value = vv;
17746         
17747         
17748         
17749     },
17750     // private
17751     reset : function(){
17752         // overridden so that last data is reset..
17753         
17754         if(this.multiple){
17755             this.clearItem();
17756             return;
17757         }
17758         
17759         this.setValue(this.originalValue);
17760         //this.clearInvalid();
17761         this.lastData = false;
17762         if (this.view) {
17763             this.view.clearSelections();
17764         }
17765         
17766         this.validate();
17767     },
17768     // private
17769     findRecord : function(prop, value){
17770         var record;
17771         if(this.store.getCount() > 0){
17772             this.store.each(function(r){
17773                 if(r.data[prop] == value){
17774                     record = r;
17775                     return false;
17776                 }
17777                 return true;
17778             });
17779         }
17780         return record;
17781     },
17782     
17783     getName: function()
17784     {
17785         // returns hidden if it's set..
17786         if (!this.rendered) {return ''};
17787         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17788         
17789     },
17790     // private
17791     onViewMove : function(e, t){
17792         this.inKeyMode = false;
17793     },
17794
17795     // private
17796     onViewOver : function(e, t){
17797         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17798             return;
17799         }
17800         var item = this.view.findItemFromChild(t);
17801         
17802         if(item){
17803             var index = this.view.indexOf(item);
17804             this.select(index, false);
17805         }
17806     },
17807
17808     // private
17809     onViewClick : function(view, doFocus, el, e)
17810     {
17811         var index = this.view.getSelectedIndexes()[0];
17812         
17813         var r = this.store.getAt(index);
17814         
17815         if(this.tickable){
17816             
17817             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17818                 return;
17819             }
17820             
17821             var rm = false;
17822             var _this = this;
17823             
17824             Roo.each(this.tickItems, function(v,k){
17825                 
17826                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17827                     Roo.log(v);
17828                     _this.tickItems.splice(k, 1);
17829                     
17830                     if(typeof(e) == 'undefined' && view == false){
17831                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17832                     }
17833                     
17834                     rm = true;
17835                     return;
17836                 }
17837             });
17838             
17839             if(rm){
17840                 return;
17841             }
17842             
17843             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17844                 this.tickItems.push(r.data);
17845             }
17846             
17847             if(typeof(e) == 'undefined' && view == false){
17848                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17849             }
17850                     
17851             return;
17852         }
17853         
17854         if(r){
17855             this.onSelect(r, index);
17856         }
17857         if(doFocus !== false && !this.blockFocus){
17858             this.inputEl().focus();
17859         }
17860     },
17861
17862     // private
17863     restrictHeight : function(){
17864         //this.innerList.dom.style.height = '';
17865         //var inner = this.innerList.dom;
17866         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17867         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17868         //this.list.beginUpdate();
17869         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17870         this.list.alignTo(this.inputEl(), this.listAlign);
17871         this.list.alignTo(this.inputEl(), this.listAlign);
17872         //this.list.endUpdate();
17873     },
17874
17875     // private
17876     onEmptyResults : function(){
17877         
17878         if(this.tickable && this.editable){
17879             this.hasFocus = false;
17880             this.restrictHeight();
17881             return;
17882         }
17883         
17884         this.collapse();
17885     },
17886
17887     /**
17888      * Returns true if the dropdown list is expanded, else false.
17889      */
17890     isExpanded : function(){
17891         return this.list.isVisible();
17892     },
17893
17894     /**
17895      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17896      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17897      * @param {String} value The data value of the item to select
17898      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17899      * selected item if it is not currently in view (defaults to true)
17900      * @return {Boolean} True if the value matched an item in the list, else false
17901      */
17902     selectByValue : function(v, scrollIntoView){
17903         if(v !== undefined && v !== null){
17904             var r = this.findRecord(this.valueField || this.displayField, v);
17905             if(r){
17906                 this.select(this.store.indexOf(r), scrollIntoView);
17907                 return true;
17908             }
17909         }
17910         return false;
17911     },
17912
17913     /**
17914      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17915      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17916      * @param {Number} index The zero-based index of the list item to select
17917      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17918      * selected item if it is not currently in view (defaults to true)
17919      */
17920     select : function(index, scrollIntoView){
17921         this.selectedIndex = index;
17922         this.view.select(index);
17923         if(scrollIntoView !== false){
17924             var el = this.view.getNode(index);
17925             /*
17926              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17927              */
17928             if(el){
17929                 this.list.scrollChildIntoView(el, false);
17930             }
17931         }
17932     },
17933
17934     // private
17935     selectNext : function(){
17936         var ct = this.store.getCount();
17937         if(ct > 0){
17938             if(this.selectedIndex == -1){
17939                 this.select(0);
17940             }else if(this.selectedIndex < ct-1){
17941                 this.select(this.selectedIndex+1);
17942             }
17943         }
17944     },
17945
17946     // private
17947     selectPrev : function(){
17948         var ct = this.store.getCount();
17949         if(ct > 0){
17950             if(this.selectedIndex == -1){
17951                 this.select(0);
17952             }else if(this.selectedIndex != 0){
17953                 this.select(this.selectedIndex-1);
17954             }
17955         }
17956     },
17957
17958     // private
17959     onKeyUp : function(e){
17960         if(this.editable !== false && !e.isSpecialKey()){
17961             this.lastKey = e.getKey();
17962             this.dqTask.delay(this.queryDelay);
17963         }
17964     },
17965
17966     // private
17967     validateBlur : function(){
17968         return !this.list || !this.list.isVisible();   
17969     },
17970
17971     // private
17972     initQuery : function(){
17973         
17974         var v = this.getRawValue();
17975         
17976         if(this.tickable && this.editable){
17977             v = this.tickableInputEl().getValue();
17978         }
17979         
17980         this.doQuery(v);
17981     },
17982
17983     // private
17984     doForce : function(){
17985         if(this.inputEl().dom.value.length > 0){
17986             this.inputEl().dom.value =
17987                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17988              
17989         }
17990     },
17991
17992     /**
17993      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17994      * query allowing the query action to be canceled if needed.
17995      * @param {String} query The SQL query to execute
17996      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17997      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17998      * saved in the current store (defaults to false)
17999      */
18000     doQuery : function(q, forceAll){
18001         
18002         if(q === undefined || q === null){
18003             q = '';
18004         }
18005         var qe = {
18006             query: q,
18007             forceAll: forceAll,
18008             combo: this,
18009             cancel:false
18010         };
18011         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18012             return false;
18013         }
18014         q = qe.query;
18015         
18016         forceAll = qe.forceAll;
18017         if(forceAll === true || (q.length >= this.minChars)){
18018             
18019             this.hasQuery = true;
18020             
18021             if(this.lastQuery != q || this.alwaysQuery){
18022                 this.lastQuery = q;
18023                 if(this.mode == 'local'){
18024                     this.selectedIndex = -1;
18025                     if(forceAll){
18026                         this.store.clearFilter();
18027                     }else{
18028                         
18029                         if(this.specialFilter){
18030                             this.fireEvent('specialfilter', this);
18031                             this.onLoad();
18032                             return;
18033                         }
18034                         
18035                         this.store.filter(this.displayField, q);
18036                     }
18037                     
18038                     this.store.fireEvent("datachanged", this.store);
18039                     
18040                     this.onLoad();
18041                     
18042                     
18043                 }else{
18044                     
18045                     this.store.baseParams[this.queryParam] = q;
18046                     
18047                     var options = {params : this.getParams(q)};
18048                     
18049                     if(this.loadNext){
18050                         options.add = true;
18051                         options.params.start = this.page * this.pageSize;
18052                     }
18053                     
18054                     this.store.load(options);
18055                     
18056                     /*
18057                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18058                      *  we should expand the list on onLoad
18059                      *  so command out it
18060                      */
18061 //                    this.expand();
18062                 }
18063             }else{
18064                 this.selectedIndex = -1;
18065                 this.onLoad();   
18066             }
18067         }
18068         
18069         this.loadNext = false;
18070     },
18071     
18072     // private
18073     getParams : function(q){
18074         var p = {};
18075         //p[this.queryParam] = q;
18076         
18077         if(this.pageSize){
18078             p.start = 0;
18079             p.limit = this.pageSize;
18080         }
18081         return p;
18082     },
18083
18084     /**
18085      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18086      */
18087     collapse : function(){
18088         if(!this.isExpanded()){
18089             return;
18090         }
18091         
18092         this.list.hide();
18093         
18094         this.hasFocus = false;
18095         
18096         if(this.tickable){
18097             this.okBtn.hide();
18098             this.cancelBtn.hide();
18099             this.trigger.show();
18100             
18101             if(this.editable){
18102                 this.tickableInputEl().dom.value = '';
18103                 this.tickableInputEl().blur();
18104             }
18105             
18106         }
18107         
18108         Roo.get(document).un('mousedown', this.collapseIf, this);
18109         Roo.get(document).un('mousewheel', this.collapseIf, this);
18110         if (!this.editable) {
18111             Roo.get(document).un('keydown', this.listKeyPress, this);
18112         }
18113         this.fireEvent('collapse', this);
18114         
18115         this.validate();
18116     },
18117
18118     // private
18119     collapseIf : function(e){
18120         var in_combo  = e.within(this.el);
18121         var in_list =  e.within(this.list);
18122         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18123         
18124         if (in_combo || in_list || is_list) {
18125             //e.stopPropagation();
18126             return;
18127         }
18128         
18129         if(this.tickable){
18130             this.onTickableFooterButtonClick(e, false, false);
18131         }
18132
18133         this.collapse();
18134         
18135     },
18136
18137     /**
18138      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18139      */
18140     expand : function(){
18141        
18142         if(this.isExpanded() || !this.hasFocus){
18143             return;
18144         }
18145         
18146         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18147         this.list.setWidth(lw);
18148         
18149         Roo.log('expand');
18150         
18151         this.list.show();
18152         
18153         this.restrictHeight();
18154         
18155         if(this.tickable){
18156             
18157             this.tickItems = Roo.apply([], this.item);
18158             
18159             this.okBtn.show();
18160             this.cancelBtn.show();
18161             this.trigger.hide();
18162             
18163             if(this.editable){
18164                 this.tickableInputEl().focus();
18165             }
18166             
18167         }
18168         
18169         Roo.get(document).on('mousedown', this.collapseIf, this);
18170         Roo.get(document).on('mousewheel', this.collapseIf, this);
18171         if (!this.editable) {
18172             Roo.get(document).on('keydown', this.listKeyPress, this);
18173         }
18174         
18175         this.fireEvent('expand', this);
18176     },
18177
18178     // private
18179     // Implements the default empty TriggerField.onTriggerClick function
18180     onTriggerClick : function(e)
18181     {
18182         Roo.log('trigger click');
18183         
18184         if(this.disabled || !this.triggerList){
18185             return;
18186         }
18187         
18188         this.page = 0;
18189         this.loadNext = false;
18190         
18191         if(this.isExpanded()){
18192             this.collapse();
18193             if (!this.blockFocus) {
18194                 this.inputEl().focus();
18195             }
18196             
18197         }else {
18198             this.hasFocus = true;
18199             if(this.triggerAction == 'all') {
18200                 this.doQuery(this.allQuery, true);
18201             } else {
18202                 this.doQuery(this.getRawValue());
18203             }
18204             if (!this.blockFocus) {
18205                 this.inputEl().focus();
18206             }
18207         }
18208     },
18209     
18210     onTickableTriggerClick : function(e)
18211     {
18212         if(this.disabled){
18213             return;
18214         }
18215         
18216         this.page = 0;
18217         this.loadNext = false;
18218         this.hasFocus = true;
18219         
18220         if(this.triggerAction == 'all') {
18221             this.doQuery(this.allQuery, true);
18222         } else {
18223             this.doQuery(this.getRawValue());
18224         }
18225     },
18226     
18227     onSearchFieldClick : function(e)
18228     {
18229         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18230             this.onTickableFooterButtonClick(e, false, false);
18231             return;
18232         }
18233         
18234         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18235             return;
18236         }
18237         
18238         this.page = 0;
18239         this.loadNext = false;
18240         this.hasFocus = true;
18241         
18242         if(this.triggerAction == 'all') {
18243             this.doQuery(this.allQuery, true);
18244         } else {
18245             this.doQuery(this.getRawValue());
18246         }
18247     },
18248     
18249     listKeyPress : function(e)
18250     {
18251         //Roo.log('listkeypress');
18252         // scroll to first matching element based on key pres..
18253         if (e.isSpecialKey()) {
18254             return false;
18255         }
18256         var k = String.fromCharCode(e.getKey()).toUpperCase();
18257         //Roo.log(k);
18258         var match  = false;
18259         var csel = this.view.getSelectedNodes();
18260         var cselitem = false;
18261         if (csel.length) {
18262             var ix = this.view.indexOf(csel[0]);
18263             cselitem  = this.store.getAt(ix);
18264             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18265                 cselitem = false;
18266             }
18267             
18268         }
18269         
18270         this.store.each(function(v) { 
18271             if (cselitem) {
18272                 // start at existing selection.
18273                 if (cselitem.id == v.id) {
18274                     cselitem = false;
18275                 }
18276                 return true;
18277             }
18278                 
18279             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18280                 match = this.store.indexOf(v);
18281                 return false;
18282             }
18283             return true;
18284         }, this);
18285         
18286         if (match === false) {
18287             return true; // no more action?
18288         }
18289         // scroll to?
18290         this.view.select(match);
18291         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18292         sn.scrollIntoView(sn.dom.parentNode, false);
18293     },
18294     
18295     onViewScroll : function(e, t){
18296         
18297         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){
18298             return;
18299         }
18300         
18301         this.hasQuery = true;
18302         
18303         this.loading = this.list.select('.loading', true).first();
18304         
18305         if(this.loading === null){
18306             this.list.createChild({
18307                 tag: 'div',
18308                 cls: 'loading roo-select2-more-results roo-select2-active',
18309                 html: 'Loading more results...'
18310             });
18311             
18312             this.loading = this.list.select('.loading', true).first();
18313             
18314             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18315             
18316             this.loading.hide();
18317         }
18318         
18319         this.loading.show();
18320         
18321         var _combo = this;
18322         
18323         this.page++;
18324         this.loadNext = true;
18325         
18326         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18327         
18328         return;
18329     },
18330     
18331     addItem : function(o)
18332     {   
18333         var dv = ''; // display value
18334         
18335         if (this.displayField) {
18336             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18337         } else {
18338             // this is an error condition!!!
18339             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18340         }
18341         
18342         if(!dv.length){
18343             return;
18344         }
18345         
18346         var choice = this.choices.createChild({
18347             tag: 'li',
18348             cls: 'roo-select2-search-choice',
18349             cn: [
18350                 {
18351                     tag: 'div',
18352                     html: dv
18353                 },
18354                 {
18355                     tag: 'a',
18356                     href: '#',
18357                     cls: 'roo-select2-search-choice-close fa fa-times',
18358                     tabindex: '-1'
18359                 }
18360             ]
18361             
18362         }, this.searchField);
18363         
18364         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18365         
18366         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18367         
18368         this.item.push(o);
18369         
18370         this.lastData = o;
18371         
18372         this.syncValue();
18373         
18374         this.inputEl().dom.value = '';
18375         
18376         this.validate();
18377     },
18378     
18379     onRemoveItem : function(e, _self, o)
18380     {
18381         e.preventDefault();
18382         
18383         this.lastItem = Roo.apply([], this.item);
18384         
18385         var index = this.item.indexOf(o.data) * 1;
18386         
18387         if( index < 0){
18388             Roo.log('not this item?!');
18389             return;
18390         }
18391         
18392         this.item.splice(index, 1);
18393         o.item.remove();
18394         
18395         this.syncValue();
18396         
18397         this.fireEvent('remove', this, e);
18398         
18399         this.validate();
18400         
18401     },
18402     
18403     syncValue : function()
18404     {
18405         if(!this.item.length){
18406             this.clearValue();
18407             return;
18408         }
18409             
18410         var value = [];
18411         var _this = this;
18412         Roo.each(this.item, function(i){
18413             if(_this.valueField){
18414                 value.push(i[_this.valueField]);
18415                 return;
18416             }
18417
18418             value.push(i);
18419         });
18420
18421         this.value = value.join(',');
18422
18423         if(this.hiddenField){
18424             this.hiddenField.dom.value = this.value;
18425         }
18426         
18427         this.store.fireEvent("datachanged", this.store);
18428         
18429         this.validate();
18430     },
18431     
18432     clearItem : function()
18433     {
18434         if(!this.multiple){
18435             return;
18436         }
18437         
18438         this.item = [];
18439         
18440         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18441            c.remove();
18442         });
18443         
18444         this.syncValue();
18445         
18446         this.validate();
18447         
18448         if(this.tickable && !Roo.isTouch){
18449             this.view.refresh();
18450         }
18451     },
18452     
18453     inputEl: function ()
18454     {
18455         if(Roo.isIOS && this.useNativeIOS){
18456             return this.el.select('select.roo-ios-select', true).first();
18457         }
18458         
18459         if(Roo.isTouch && this.mobileTouchView){
18460             return this.el.select('input.form-control',true).first();
18461         }
18462         
18463         if(this.tickable){
18464             return this.searchField;
18465         }
18466         
18467         return this.el.select('input.form-control',true).first();
18468     },
18469     
18470     onTickableFooterButtonClick : function(e, btn, el)
18471     {
18472         e.preventDefault();
18473         
18474         this.lastItem = Roo.apply([], this.item);
18475         
18476         if(btn && btn.name == 'cancel'){
18477             this.tickItems = Roo.apply([], this.item);
18478             this.collapse();
18479             return;
18480         }
18481         
18482         this.clearItem();
18483         
18484         var _this = this;
18485         
18486         Roo.each(this.tickItems, function(o){
18487             _this.addItem(o);
18488         });
18489         
18490         this.collapse();
18491         
18492     },
18493     
18494     validate : function()
18495     {
18496         if(this.getVisibilityEl().hasClass('hidden')){
18497             return true;
18498         }
18499         
18500         var v = this.getRawValue();
18501         
18502         if(this.multiple){
18503             v = this.getValue();
18504         }
18505         
18506         if(this.disabled || this.allowBlank || v.length){
18507             this.markValid();
18508             return true;
18509         }
18510         
18511         this.markInvalid();
18512         return false;
18513     },
18514     
18515     tickableInputEl : function()
18516     {
18517         if(!this.tickable || !this.editable){
18518             return this.inputEl();
18519         }
18520         
18521         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18522     },
18523     
18524     
18525     getAutoCreateTouchView : function()
18526     {
18527         var id = Roo.id();
18528         
18529         var cfg = {
18530             cls: 'form-group' //input-group
18531         };
18532         
18533         var input =  {
18534             tag: 'input',
18535             id : id,
18536             type : this.inputType,
18537             cls : 'form-control x-combo-noedit',
18538             autocomplete: 'new-password',
18539             placeholder : this.placeholder || '',
18540             readonly : true
18541         };
18542         
18543         if (this.name) {
18544             input.name = this.name;
18545         }
18546         
18547         if (this.size) {
18548             input.cls += ' input-' + this.size;
18549         }
18550         
18551         if (this.disabled) {
18552             input.disabled = true;
18553         }
18554         
18555         var inputblock = {
18556             cls : 'roo-combobox-wrap',
18557             cn : [
18558                 input
18559             ]
18560         };
18561         
18562         if(this.before){
18563             inputblock.cls += ' input-group';
18564             
18565             inputblock.cn.unshift({
18566                 tag :'span',
18567                 cls : 'input-group-addon input-group-prepend input-group-text',
18568                 html : this.before
18569             });
18570         }
18571         
18572         if(this.removable && !this.multiple){
18573             inputblock.cls += ' roo-removable';
18574             
18575             inputblock.cn.push({
18576                 tag: 'button',
18577                 html : 'x',
18578                 cls : 'roo-combo-removable-btn close'
18579             });
18580         }
18581
18582         if(this.hasFeedback && !this.allowBlank){
18583             
18584             inputblock.cls += ' has-feedback';
18585             
18586             inputblock.cn.push({
18587                 tag: 'span',
18588                 cls: 'glyphicon form-control-feedback'
18589             });
18590             
18591         }
18592         
18593         if (this.after) {
18594             
18595             inputblock.cls += (this.before) ? '' : ' input-group';
18596             
18597             inputblock.cn.push({
18598                 tag :'span',
18599                 cls : 'input-group-addon input-group-append input-group-text',
18600                 html : this.after
18601             });
18602         }
18603
18604         
18605         var ibwrap = inputblock;
18606         
18607         if(this.multiple){
18608             ibwrap = {
18609                 tag: 'ul',
18610                 cls: 'roo-select2-choices',
18611                 cn:[
18612                     {
18613                         tag: 'li',
18614                         cls: 'roo-select2-search-field',
18615                         cn: [
18616
18617                             inputblock
18618                         ]
18619                     }
18620                 ]
18621             };
18622         
18623             
18624         }
18625         
18626         var combobox = {
18627             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18628             cn: [
18629                 {
18630                     tag: 'input',
18631                     type : 'hidden',
18632                     cls: 'form-hidden-field'
18633                 },
18634                 ibwrap
18635             ]
18636         };
18637         
18638         if(!this.multiple && this.showToggleBtn){
18639             
18640             var caret = {
18641                 cls: 'caret'
18642             };
18643             
18644             if (this.caret != false) {
18645                 caret = {
18646                      tag: 'i',
18647                      cls: 'fa fa-' + this.caret
18648                 };
18649                 
18650             }
18651             
18652             combobox.cn.push({
18653                 tag :'span',
18654                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18655                 cn : [
18656                     Roo.bootstrap.version == 3 ? caret : '',
18657                     {
18658                         tag: 'span',
18659                         cls: 'combobox-clear',
18660                         cn  : [
18661                             {
18662                                 tag : 'i',
18663                                 cls: 'icon-remove'
18664                             }
18665                         ]
18666                     }
18667                 ]
18668
18669             })
18670         }
18671         
18672         if(this.multiple){
18673             combobox.cls += ' roo-select2-container-multi';
18674         }
18675         
18676         var required =  this.allowBlank ?  {
18677                     tag : 'i',
18678                     style: 'display: none'
18679                 } : {
18680                    tag : 'i',
18681                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18682                    tooltip : 'This field is required'
18683                 };
18684         
18685         var align = this.labelAlign || this.parentLabelAlign();
18686         
18687         if (align ==='left' && this.fieldLabel.length) {
18688
18689             cfg.cn = [
18690                 required,
18691                 {
18692                     tag: 'label',
18693                     cls : 'control-label col-form-label',
18694                     html : this.fieldLabel
18695
18696                 },
18697                 {
18698                     cls : 'roo-combobox-wrap ', 
18699                     cn: [
18700                         combobox
18701                     ]
18702                 }
18703             ];
18704             
18705             var labelCfg = cfg.cn[1];
18706             var contentCfg = cfg.cn[2];
18707             
18708
18709             if(this.indicatorpos == 'right'){
18710                 cfg.cn = [
18711                     {
18712                         tag: 'label',
18713                         'for' :  id,
18714                         cls : 'control-label col-form-label',
18715                         cn : [
18716                             {
18717                                 tag : 'span',
18718                                 html : this.fieldLabel
18719                             },
18720                             required
18721                         ]
18722                     },
18723                     {
18724                         cls : "roo-combobox-wrap ",
18725                         cn: [
18726                             combobox
18727                         ]
18728                     }
18729
18730                 ];
18731                 
18732                 labelCfg = cfg.cn[0];
18733                 contentCfg = cfg.cn[1];
18734             }
18735             
18736            
18737             
18738             if(this.labelWidth > 12){
18739                 labelCfg.style = "width: " + this.labelWidth + 'px';
18740             }
18741            
18742             if(this.labelWidth < 13 && this.labelmd == 0){
18743                 this.labelmd = this.labelWidth;
18744             }
18745             
18746             if(this.labellg > 0){
18747                 labelCfg.cls += ' col-lg-' + this.labellg;
18748                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18749             }
18750             
18751             if(this.labelmd > 0){
18752                 labelCfg.cls += ' col-md-' + this.labelmd;
18753                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18754             }
18755             
18756             if(this.labelsm > 0){
18757                 labelCfg.cls += ' col-sm-' + this.labelsm;
18758                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18759             }
18760             
18761             if(this.labelxs > 0){
18762                 labelCfg.cls += ' col-xs-' + this.labelxs;
18763                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18764             }
18765                 
18766                 
18767         } else if ( this.fieldLabel.length) {
18768             cfg.cn = [
18769                required,
18770                 {
18771                     tag: 'label',
18772                     cls : 'control-label',
18773                     html : this.fieldLabel
18774
18775                 },
18776                 {
18777                     cls : '', 
18778                     cn: [
18779                         combobox
18780                     ]
18781                 }
18782             ];
18783             
18784             if(this.indicatorpos == 'right'){
18785                 cfg.cn = [
18786                     {
18787                         tag: 'label',
18788                         cls : 'control-label',
18789                         html : this.fieldLabel,
18790                         cn : [
18791                             required
18792                         ]
18793                     },
18794                     {
18795                         cls : '', 
18796                         cn: [
18797                             combobox
18798                         ]
18799                     }
18800                 ];
18801             }
18802         } else {
18803             cfg.cn = combobox;    
18804         }
18805         
18806         
18807         var settings = this;
18808         
18809         ['xs','sm','md','lg'].map(function(size){
18810             if (settings[size]) {
18811                 cfg.cls += ' col-' + size + '-' + settings[size];
18812             }
18813         });
18814         
18815         return cfg;
18816     },
18817     
18818     initTouchView : function()
18819     {
18820         this.renderTouchView();
18821         
18822         this.touchViewEl.on('scroll', function(){
18823             this.el.dom.scrollTop = 0;
18824         }, this);
18825         
18826         this.originalValue = this.getValue();
18827         
18828         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18829         
18830         this.inputEl().on("click", this.showTouchView, this);
18831         if (this.triggerEl) {
18832             this.triggerEl.on("click", this.showTouchView, this);
18833         }
18834         
18835         
18836         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18837         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18838         
18839         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18840         
18841         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18842         this.store.on('load', this.onTouchViewLoad, this);
18843         this.store.on('loadexception', this.onTouchViewLoadException, this);
18844         
18845         if(this.hiddenName){
18846             
18847             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18848             
18849             this.hiddenField.dom.value =
18850                 this.hiddenValue !== undefined ? this.hiddenValue :
18851                 this.value !== undefined ? this.value : '';
18852         
18853             this.el.dom.removeAttribute('name');
18854             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18855         }
18856         
18857         if(this.multiple){
18858             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18859             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18860         }
18861         
18862         if(this.removable && !this.multiple){
18863             var close = this.closeTriggerEl();
18864             if(close){
18865                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18866                 close.on('click', this.removeBtnClick, this, close);
18867             }
18868         }
18869         /*
18870          * fix the bug in Safari iOS8
18871          */
18872         this.inputEl().on("focus", function(e){
18873             document.activeElement.blur();
18874         }, this);
18875         
18876         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18877         
18878         return;
18879         
18880         
18881     },
18882     
18883     renderTouchView : function()
18884     {
18885         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18886         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18887         
18888         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18889         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         
18891         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18892         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893         this.touchViewBodyEl.setStyle('overflow', 'auto');
18894         
18895         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18896         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18897         
18898         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18899         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18900         
18901     },
18902     
18903     showTouchView : function()
18904     {
18905         if(this.disabled){
18906             return;
18907         }
18908         
18909         this.touchViewHeaderEl.hide();
18910
18911         if(this.modalTitle.length){
18912             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18913             this.touchViewHeaderEl.show();
18914         }
18915
18916         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18917         this.touchViewEl.show();
18918
18919         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18920         
18921         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18922         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18923
18924         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18925
18926         if(this.modalTitle.length){
18927             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18928         }
18929         
18930         this.touchViewBodyEl.setHeight(bodyHeight);
18931
18932         if(this.animate){
18933             var _this = this;
18934             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18935         }else{
18936             this.touchViewEl.addClass(['in','show']);
18937         }
18938         
18939         if(this._touchViewMask){
18940             Roo.get(document.body).addClass("x-body-masked");
18941             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18942             this._touchViewMask.setStyle('z-index', 10000);
18943             this._touchViewMask.addClass('show');
18944         }
18945         
18946         this.doTouchViewQuery();
18947         
18948     },
18949     
18950     hideTouchView : function()
18951     {
18952         this.touchViewEl.removeClass(['in','show']);
18953
18954         if(this.animate){
18955             var _this = this;
18956             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18957         }else{
18958             this.touchViewEl.setStyle('display', 'none');
18959         }
18960         
18961         if(this._touchViewMask){
18962             this._touchViewMask.removeClass('show');
18963             Roo.get(document.body).removeClass("x-body-masked");
18964         }
18965     },
18966     
18967     setTouchViewValue : function()
18968     {
18969         if(this.multiple){
18970             this.clearItem();
18971         
18972             var _this = this;
18973
18974             Roo.each(this.tickItems, function(o){
18975                 this.addItem(o);
18976             }, this);
18977         }
18978         
18979         this.hideTouchView();
18980     },
18981     
18982     doTouchViewQuery : function()
18983     {
18984         var qe = {
18985             query: '',
18986             forceAll: true,
18987             combo: this,
18988             cancel:false
18989         };
18990         
18991         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18992             return false;
18993         }
18994         
18995         if(!this.alwaysQuery || this.mode == 'local'){
18996             this.onTouchViewLoad();
18997             return;
18998         }
18999         
19000         this.store.load();
19001     },
19002     
19003     onTouchViewBeforeLoad : function(combo,opts)
19004     {
19005         return;
19006     },
19007
19008     // private
19009     onTouchViewLoad : function()
19010     {
19011         if(this.store.getCount() < 1){
19012             this.onTouchViewEmptyResults();
19013             return;
19014         }
19015         
19016         this.clearTouchView();
19017         
19018         var rawValue = this.getRawValue();
19019         
19020         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19021         
19022         this.tickItems = [];
19023         
19024         this.store.data.each(function(d, rowIndex){
19025             var row = this.touchViewListGroup.createChild(template);
19026             
19027             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19028                 row.addClass(d.data.cls);
19029             }
19030             
19031             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19032                 var cfg = {
19033                     data : d.data,
19034                     html : d.data[this.displayField]
19035                 };
19036                 
19037                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19038                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19039                 }
19040             }
19041             row.removeClass('selected');
19042             if(!this.multiple && this.valueField &&
19043                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19044             {
19045                 // radio buttons..
19046                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19047                 row.addClass('selected');
19048             }
19049             
19050             if(this.multiple && this.valueField &&
19051                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19052             {
19053                 
19054                 // checkboxes...
19055                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19056                 this.tickItems.push(d.data);
19057             }
19058             
19059             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19060             
19061         }, this);
19062         
19063         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19064         
19065         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19066
19067         if(this.modalTitle.length){
19068             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19069         }
19070
19071         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19072         
19073         if(this.mobile_restrict_height && listHeight < bodyHeight){
19074             this.touchViewBodyEl.setHeight(listHeight);
19075         }
19076         
19077         var _this = this;
19078         
19079         if(firstChecked && listHeight > bodyHeight){
19080             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19081         }
19082         
19083     },
19084     
19085     onTouchViewLoadException : function()
19086     {
19087         this.hideTouchView();
19088     },
19089     
19090     onTouchViewEmptyResults : function()
19091     {
19092         this.clearTouchView();
19093         
19094         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19095         
19096         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19097         
19098     },
19099     
19100     clearTouchView : function()
19101     {
19102         this.touchViewListGroup.dom.innerHTML = '';
19103     },
19104     
19105     onTouchViewClick : function(e, el, o)
19106     {
19107         e.preventDefault();
19108         
19109         var row = o.row;
19110         var rowIndex = o.rowIndex;
19111         
19112         var r = this.store.getAt(rowIndex);
19113         
19114         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19115             
19116             if(!this.multiple){
19117                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19118                     c.dom.removeAttribute('checked');
19119                 }, this);
19120
19121                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19122
19123                 this.setFromData(r.data);
19124
19125                 var close = this.closeTriggerEl();
19126
19127                 if(close){
19128                     close.show();
19129                 }
19130
19131                 this.hideTouchView();
19132
19133                 this.fireEvent('select', this, r, rowIndex);
19134
19135                 return;
19136             }
19137
19138             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19139                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19140                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19141                 return;
19142             }
19143
19144             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19145             this.addItem(r.data);
19146             this.tickItems.push(r.data);
19147         }
19148     },
19149     
19150     getAutoCreateNativeIOS : function()
19151     {
19152         var cfg = {
19153             cls: 'form-group' //input-group,
19154         };
19155         
19156         var combobox =  {
19157             tag: 'select',
19158             cls : 'roo-ios-select'
19159         };
19160         
19161         if (this.name) {
19162             combobox.name = this.name;
19163         }
19164         
19165         if (this.disabled) {
19166             combobox.disabled = true;
19167         }
19168         
19169         var settings = this;
19170         
19171         ['xs','sm','md','lg'].map(function(size){
19172             if (settings[size]) {
19173                 cfg.cls += ' col-' + size + '-' + settings[size];
19174             }
19175         });
19176         
19177         cfg.cn = combobox;
19178         
19179         return cfg;
19180         
19181     },
19182     
19183     initIOSView : function()
19184     {
19185         this.store.on('load', this.onIOSViewLoad, this);
19186         
19187         return;
19188     },
19189     
19190     onIOSViewLoad : function()
19191     {
19192         if(this.store.getCount() < 1){
19193             return;
19194         }
19195         
19196         this.clearIOSView();
19197         
19198         if(this.allowBlank) {
19199             
19200             var default_text = '-- SELECT --';
19201             
19202             if(this.placeholder.length){
19203                 default_text = this.placeholder;
19204             }
19205             
19206             if(this.emptyTitle.length){
19207                 default_text += ' - ' + this.emptyTitle + ' -';
19208             }
19209             
19210             var opt = this.inputEl().createChild({
19211                 tag: 'option',
19212                 value : 0,
19213                 html : default_text
19214             });
19215             
19216             var o = {};
19217             o[this.valueField] = 0;
19218             o[this.displayField] = default_text;
19219             
19220             this.ios_options.push({
19221                 data : o,
19222                 el : opt
19223             });
19224             
19225         }
19226         
19227         this.store.data.each(function(d, rowIndex){
19228             
19229             var html = '';
19230             
19231             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19232                 html = d.data[this.displayField];
19233             }
19234             
19235             var value = '';
19236             
19237             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19238                 value = d.data[this.valueField];
19239             }
19240             
19241             var option = {
19242                 tag: 'option',
19243                 value : value,
19244                 html : html
19245             };
19246             
19247             if(this.value == d.data[this.valueField]){
19248                 option['selected'] = true;
19249             }
19250             
19251             var opt = this.inputEl().createChild(option);
19252             
19253             this.ios_options.push({
19254                 data : d.data,
19255                 el : opt
19256             });
19257             
19258         }, this);
19259         
19260         this.inputEl().on('change', function(){
19261            this.fireEvent('select', this);
19262         }, this);
19263         
19264     },
19265     
19266     clearIOSView: function()
19267     {
19268         this.inputEl().dom.innerHTML = '';
19269         
19270         this.ios_options = [];
19271     },
19272     
19273     setIOSValue: function(v)
19274     {
19275         this.value = v;
19276         
19277         if(!this.ios_options){
19278             return;
19279         }
19280         
19281         Roo.each(this.ios_options, function(opts){
19282            
19283            opts.el.dom.removeAttribute('selected');
19284            
19285            if(opts.data[this.valueField] != v){
19286                return;
19287            }
19288            
19289            opts.el.dom.setAttribute('selected', true);
19290            
19291         }, this);
19292     }
19293
19294     /** 
19295     * @cfg {Boolean} grow 
19296     * @hide 
19297     */
19298     /** 
19299     * @cfg {Number} growMin 
19300     * @hide 
19301     */
19302     /** 
19303     * @cfg {Number} growMax 
19304     * @hide 
19305     */
19306     /**
19307      * @hide
19308      * @method autoSize
19309      */
19310 });
19311
19312 Roo.apply(Roo.bootstrap.ComboBox,  {
19313     
19314     header : {
19315         tag: 'div',
19316         cls: 'modal-header',
19317         cn: [
19318             {
19319                 tag: 'h4',
19320                 cls: 'modal-title'
19321             }
19322         ]
19323     },
19324     
19325     body : {
19326         tag: 'div',
19327         cls: 'modal-body',
19328         cn: [
19329             {
19330                 tag: 'ul',
19331                 cls: 'list-group'
19332             }
19333         ]
19334     },
19335     
19336     listItemRadio : {
19337         tag: 'li',
19338         cls: 'list-group-item',
19339         cn: [
19340             {
19341                 tag: 'span',
19342                 cls: 'roo-combobox-list-group-item-value'
19343             },
19344             {
19345                 tag: 'div',
19346                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19347                 cn: [
19348                     {
19349                         tag: 'input',
19350                         type: 'radio'
19351                     },
19352                     {
19353                         tag: 'label'
19354                     }
19355                 ]
19356             }
19357         ]
19358     },
19359     
19360     listItemCheckbox : {
19361         tag: 'li',
19362         cls: 'list-group-item',
19363         cn: [
19364             {
19365                 tag: 'span',
19366                 cls: 'roo-combobox-list-group-item-value'
19367             },
19368             {
19369                 tag: 'div',
19370                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19371                 cn: [
19372                     {
19373                         tag: 'input',
19374                         type: 'checkbox'
19375                     },
19376                     {
19377                         tag: 'label'
19378                     }
19379                 ]
19380             }
19381         ]
19382     },
19383     
19384     emptyResult : {
19385         tag: 'div',
19386         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19387     },
19388     
19389     footer : {
19390         tag: 'div',
19391         cls: 'modal-footer',
19392         cn: [
19393             {
19394                 tag: 'div',
19395                 cls: 'row',
19396                 cn: [
19397                     {
19398                         tag: 'div',
19399                         cls: 'col-xs-6 text-left',
19400                         cn: {
19401                             tag: 'button',
19402                             cls: 'btn btn-danger roo-touch-view-cancel',
19403                             html: 'Cancel'
19404                         }
19405                     },
19406                     {
19407                         tag: 'div',
19408                         cls: 'col-xs-6 text-right',
19409                         cn: {
19410                             tag: 'button',
19411                             cls: 'btn btn-success roo-touch-view-ok',
19412                             html: 'OK'
19413                         }
19414                     }
19415                 ]
19416             }
19417         ]
19418         
19419     }
19420 });
19421
19422 Roo.apply(Roo.bootstrap.ComboBox,  {
19423     
19424     touchViewTemplate : {
19425         tag: 'div',
19426         cls: 'modal fade roo-combobox-touch-view',
19427         cn: [
19428             {
19429                 tag: 'div',
19430                 cls: 'modal-dialog',
19431                 style : 'position:fixed', // we have to fix position....
19432                 cn: [
19433                     {
19434                         tag: 'div',
19435                         cls: 'modal-content',
19436                         cn: [
19437                             Roo.bootstrap.ComboBox.header,
19438                             Roo.bootstrap.ComboBox.body,
19439                             Roo.bootstrap.ComboBox.footer
19440                         ]
19441                     }
19442                 ]
19443             }
19444         ]
19445     }
19446 });/*
19447  * Based on:
19448  * Ext JS Library 1.1.1
19449  * Copyright(c) 2006-2007, Ext JS, LLC.
19450  *
19451  * Originally Released Under LGPL - original licence link has changed is not relivant.
19452  *
19453  * Fork - LGPL
19454  * <script type="text/javascript">
19455  */
19456
19457 /**
19458  * @class Roo.View
19459  * @extends Roo.util.Observable
19460  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19461  * This class also supports single and multi selection modes. <br>
19462  * Create a data model bound view:
19463  <pre><code>
19464  var store = new Roo.data.Store(...);
19465
19466  var view = new Roo.View({
19467     el : "my-element",
19468     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19469  
19470     singleSelect: true,
19471     selectedClass: "ydataview-selected",
19472     store: store
19473  });
19474
19475  // listen for node click?
19476  view.on("click", function(vw, index, node, e){
19477  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19478  });
19479
19480  // load XML data
19481  dataModel.load("foobar.xml");
19482  </code></pre>
19483  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19484  * <br><br>
19485  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19486  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19487  * 
19488  * Note: old style constructor is still suported (container, template, config)
19489  * 
19490  * @constructor
19491  * Create a new View
19492  * @param {Object} config The config object
19493  * 
19494  */
19495 Roo.View = function(config, depreciated_tpl, depreciated_config){
19496     
19497     this.parent = false;
19498     
19499     if (typeof(depreciated_tpl) == 'undefined') {
19500         // new way.. - universal constructor.
19501         Roo.apply(this, config);
19502         this.el  = Roo.get(this.el);
19503     } else {
19504         // old format..
19505         this.el  = Roo.get(config);
19506         this.tpl = depreciated_tpl;
19507         Roo.apply(this, depreciated_config);
19508     }
19509     this.wrapEl  = this.el.wrap().wrap();
19510     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19511     
19512     
19513     if(typeof(this.tpl) == "string"){
19514         this.tpl = new Roo.Template(this.tpl);
19515     } else {
19516         // support xtype ctors..
19517         this.tpl = new Roo.factory(this.tpl, Roo);
19518     }
19519     
19520     
19521     this.tpl.compile();
19522     
19523     /** @private */
19524     this.addEvents({
19525         /**
19526          * @event beforeclick
19527          * Fires before a click is processed. Returns false to cancel the default action.
19528          * @param {Roo.View} this
19529          * @param {Number} index The index of the target node
19530          * @param {HTMLElement} node The target node
19531          * @param {Roo.EventObject} e The raw event object
19532          */
19533             "beforeclick" : true,
19534         /**
19535          * @event click
19536          * Fires when a template node is clicked.
19537          * @param {Roo.View} this
19538          * @param {Number} index The index of the target node
19539          * @param {HTMLElement} node The target node
19540          * @param {Roo.EventObject} e The raw event object
19541          */
19542             "click" : true,
19543         /**
19544          * @event dblclick
19545          * Fires when a template node is double clicked.
19546          * @param {Roo.View} this
19547          * @param {Number} index The index of the target node
19548          * @param {HTMLElement} node The target node
19549          * @param {Roo.EventObject} e The raw event object
19550          */
19551             "dblclick" : true,
19552         /**
19553          * @event contextmenu
19554          * Fires when a template node is right clicked.
19555          * @param {Roo.View} this
19556          * @param {Number} index The index of the target node
19557          * @param {HTMLElement} node The target node
19558          * @param {Roo.EventObject} e The raw event object
19559          */
19560             "contextmenu" : true,
19561         /**
19562          * @event selectionchange
19563          * Fires when the selected nodes change.
19564          * @param {Roo.View} this
19565          * @param {Array} selections Array of the selected nodes
19566          */
19567             "selectionchange" : true,
19568     
19569         /**
19570          * @event beforeselect
19571          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19572          * @param {Roo.View} this
19573          * @param {HTMLElement} node The node to be selected
19574          * @param {Array} selections Array of currently selected nodes
19575          */
19576             "beforeselect" : true,
19577         /**
19578          * @event preparedata
19579          * Fires on every row to render, to allow you to change the data.
19580          * @param {Roo.View} this
19581          * @param {Object} data to be rendered (change this)
19582          */
19583           "preparedata" : true
19584           
19585           
19586         });
19587
19588
19589
19590     this.el.on({
19591         "click": this.onClick,
19592         "dblclick": this.onDblClick,
19593         "contextmenu": this.onContextMenu,
19594         scope:this
19595     });
19596
19597     this.selections = [];
19598     this.nodes = [];
19599     this.cmp = new Roo.CompositeElementLite([]);
19600     if(this.store){
19601         this.store = Roo.factory(this.store, Roo.data);
19602         this.setStore(this.store, true);
19603     }
19604     
19605     if ( this.footer && this.footer.xtype) {
19606            
19607          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19608         
19609         this.footer.dataSource = this.store;
19610         this.footer.container = fctr;
19611         this.footer = Roo.factory(this.footer, Roo);
19612         fctr.insertFirst(this.el);
19613         
19614         // this is a bit insane - as the paging toolbar seems to detach the el..
19615 //        dom.parentNode.parentNode.parentNode
19616          // they get detached?
19617     }
19618     
19619     
19620     Roo.View.superclass.constructor.call(this);
19621     
19622     
19623 };
19624
19625 Roo.extend(Roo.View, Roo.util.Observable, {
19626     
19627      /**
19628      * @cfg {Roo.data.Store} store Data store to load data from.
19629      */
19630     store : false,
19631     
19632     /**
19633      * @cfg {String|Roo.Element} el The container element.
19634      */
19635     el : '',
19636     
19637     /**
19638      * @cfg {String|Roo.Template} tpl The template used by this View 
19639      */
19640     tpl : false,
19641     /**
19642      * @cfg {String} dataName the named area of the template to use as the data area
19643      *                          Works with domtemplates roo-name="name"
19644      */
19645     dataName: false,
19646     /**
19647      * @cfg {String} selectedClass The css class to add to selected nodes
19648      */
19649     selectedClass : "x-view-selected",
19650      /**
19651      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19652      */
19653     emptyText : "",
19654     
19655     /**
19656      * @cfg {String} text to display on mask (default Loading)
19657      */
19658     mask : false,
19659     /**
19660      * @cfg {Boolean} multiSelect Allow multiple selection
19661      */
19662     multiSelect : false,
19663     /**
19664      * @cfg {Boolean} singleSelect Allow single selection
19665      */
19666     singleSelect:  false,
19667     
19668     /**
19669      * @cfg {Boolean} toggleSelect - selecting 
19670      */
19671     toggleSelect : false,
19672     
19673     /**
19674      * @cfg {Boolean} tickable - selecting 
19675      */
19676     tickable : false,
19677     
19678     /**
19679      * Returns the element this view is bound to.
19680      * @return {Roo.Element}
19681      */
19682     getEl : function(){
19683         return this.wrapEl;
19684     },
19685     
19686     
19687
19688     /**
19689      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19690      */
19691     refresh : function(){
19692         //Roo.log('refresh');
19693         var t = this.tpl;
19694         
19695         // if we are using something like 'domtemplate', then
19696         // the what gets used is:
19697         // t.applySubtemplate(NAME, data, wrapping data..)
19698         // the outer template then get' applied with
19699         //     the store 'extra data'
19700         // and the body get's added to the
19701         //      roo-name="data" node?
19702         //      <span class='roo-tpl-{name}'></span> ?????
19703         
19704         
19705         
19706         this.clearSelections();
19707         this.el.update("");
19708         var html = [];
19709         var records = this.store.getRange();
19710         if(records.length < 1) {
19711             
19712             // is this valid??  = should it render a template??
19713             
19714             this.el.update(this.emptyText);
19715             return;
19716         }
19717         var el = this.el;
19718         if (this.dataName) {
19719             this.el.update(t.apply(this.store.meta)); //????
19720             el = this.el.child('.roo-tpl-' + this.dataName);
19721         }
19722         
19723         for(var i = 0, len = records.length; i < len; i++){
19724             var data = this.prepareData(records[i].data, i, records[i]);
19725             this.fireEvent("preparedata", this, data, i, records[i]);
19726             
19727             var d = Roo.apply({}, data);
19728             
19729             if(this.tickable){
19730                 Roo.apply(d, {'roo-id' : Roo.id()});
19731                 
19732                 var _this = this;
19733             
19734                 Roo.each(this.parent.item, function(item){
19735                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19736                         return;
19737                     }
19738                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19739                 });
19740             }
19741             
19742             html[html.length] = Roo.util.Format.trim(
19743                 this.dataName ?
19744                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19745                     t.apply(d)
19746             );
19747         }
19748         
19749         
19750         
19751         el.update(html.join(""));
19752         this.nodes = el.dom.childNodes;
19753         this.updateIndexes(0);
19754     },
19755     
19756
19757     /**
19758      * Function to override to reformat the data that is sent to
19759      * the template for each node.
19760      * DEPRICATED - use the preparedata event handler.
19761      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19762      * a JSON object for an UpdateManager bound view).
19763      */
19764     prepareData : function(data, index, record)
19765     {
19766         this.fireEvent("preparedata", this, data, index, record);
19767         return data;
19768     },
19769
19770     onUpdate : function(ds, record){
19771         // Roo.log('on update');   
19772         this.clearSelections();
19773         var index = this.store.indexOf(record);
19774         var n = this.nodes[index];
19775         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19776         n.parentNode.removeChild(n);
19777         this.updateIndexes(index, index);
19778     },
19779
19780     
19781     
19782 // --------- FIXME     
19783     onAdd : function(ds, records, index)
19784     {
19785         //Roo.log(['on Add', ds, records, index] );        
19786         this.clearSelections();
19787         if(this.nodes.length == 0){
19788             this.refresh();
19789             return;
19790         }
19791         var n = this.nodes[index];
19792         for(var i = 0, len = records.length; i < len; i++){
19793             var d = this.prepareData(records[i].data, i, records[i]);
19794             if(n){
19795                 this.tpl.insertBefore(n, d);
19796             }else{
19797                 
19798                 this.tpl.append(this.el, d);
19799             }
19800         }
19801         this.updateIndexes(index);
19802     },
19803
19804     onRemove : function(ds, record, index){
19805        // Roo.log('onRemove');
19806         this.clearSelections();
19807         var el = this.dataName  ?
19808             this.el.child('.roo-tpl-' + this.dataName) :
19809             this.el; 
19810         
19811         el.dom.removeChild(this.nodes[index]);
19812         this.updateIndexes(index);
19813     },
19814
19815     /**
19816      * Refresh an individual node.
19817      * @param {Number} index
19818      */
19819     refreshNode : function(index){
19820         this.onUpdate(this.store, this.store.getAt(index));
19821     },
19822
19823     updateIndexes : function(startIndex, endIndex){
19824         var ns = this.nodes;
19825         startIndex = startIndex || 0;
19826         endIndex = endIndex || ns.length - 1;
19827         for(var i = startIndex; i <= endIndex; i++){
19828             ns[i].nodeIndex = i;
19829         }
19830     },
19831
19832     /**
19833      * Changes the data store this view uses and refresh the view.
19834      * @param {Store} store
19835      */
19836     setStore : function(store, initial){
19837         if(!initial && this.store){
19838             this.store.un("datachanged", this.refresh);
19839             this.store.un("add", this.onAdd);
19840             this.store.un("remove", this.onRemove);
19841             this.store.un("update", this.onUpdate);
19842             this.store.un("clear", this.refresh);
19843             this.store.un("beforeload", this.onBeforeLoad);
19844             this.store.un("load", this.onLoad);
19845             this.store.un("loadexception", this.onLoad);
19846         }
19847         if(store){
19848           
19849             store.on("datachanged", this.refresh, this);
19850             store.on("add", this.onAdd, this);
19851             store.on("remove", this.onRemove, this);
19852             store.on("update", this.onUpdate, this);
19853             store.on("clear", this.refresh, this);
19854             store.on("beforeload", this.onBeforeLoad, this);
19855             store.on("load", this.onLoad, this);
19856             store.on("loadexception", this.onLoad, this);
19857         }
19858         
19859         if(store){
19860             this.refresh();
19861         }
19862     },
19863     /**
19864      * onbeforeLoad - masks the loading area.
19865      *
19866      */
19867     onBeforeLoad : function(store,opts)
19868     {
19869          //Roo.log('onBeforeLoad');   
19870         if (!opts.add) {
19871             this.el.update("");
19872         }
19873         this.el.mask(this.mask ? this.mask : "Loading" ); 
19874     },
19875     onLoad : function ()
19876     {
19877         this.el.unmask();
19878     },
19879     
19880
19881     /**
19882      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19883      * @param {HTMLElement} node
19884      * @return {HTMLElement} The template node
19885      */
19886     findItemFromChild : function(node){
19887         var el = this.dataName  ?
19888             this.el.child('.roo-tpl-' + this.dataName,true) :
19889             this.el.dom; 
19890         
19891         if(!node || node.parentNode == el){
19892                     return node;
19893             }
19894             var p = node.parentNode;
19895             while(p && p != el){
19896             if(p.parentNode == el){
19897                 return p;
19898             }
19899             p = p.parentNode;
19900         }
19901             return null;
19902     },
19903
19904     /** @ignore */
19905     onClick : function(e){
19906         var item = this.findItemFromChild(e.getTarget());
19907         if(item){
19908             var index = this.indexOf(item);
19909             if(this.onItemClick(item, index, e) !== false){
19910                 this.fireEvent("click", this, index, item, e);
19911             }
19912         }else{
19913             this.clearSelections();
19914         }
19915     },
19916
19917     /** @ignore */
19918     onContextMenu : function(e){
19919         var item = this.findItemFromChild(e.getTarget());
19920         if(item){
19921             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19922         }
19923     },
19924
19925     /** @ignore */
19926     onDblClick : function(e){
19927         var item = this.findItemFromChild(e.getTarget());
19928         if(item){
19929             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19930         }
19931     },
19932
19933     onItemClick : function(item, index, e)
19934     {
19935         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19936             return false;
19937         }
19938         if (this.toggleSelect) {
19939             var m = this.isSelected(item) ? 'unselect' : 'select';
19940             //Roo.log(m);
19941             var _t = this;
19942             _t[m](item, true, false);
19943             return true;
19944         }
19945         if(this.multiSelect || this.singleSelect){
19946             if(this.multiSelect && e.shiftKey && this.lastSelection){
19947                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19948             }else{
19949                 this.select(item, this.multiSelect && e.ctrlKey);
19950                 this.lastSelection = item;
19951             }
19952             
19953             if(!this.tickable){
19954                 e.preventDefault();
19955             }
19956             
19957         }
19958         return true;
19959     },
19960
19961     /**
19962      * Get the number of selected nodes.
19963      * @return {Number}
19964      */
19965     getSelectionCount : function(){
19966         return this.selections.length;
19967     },
19968
19969     /**
19970      * Get the currently selected nodes.
19971      * @return {Array} An array of HTMLElements
19972      */
19973     getSelectedNodes : function(){
19974         return this.selections;
19975     },
19976
19977     /**
19978      * Get the indexes of the selected nodes.
19979      * @return {Array}
19980      */
19981     getSelectedIndexes : function(){
19982         var indexes = [], s = this.selections;
19983         for(var i = 0, len = s.length; i < len; i++){
19984             indexes.push(s[i].nodeIndex);
19985         }
19986         return indexes;
19987     },
19988
19989     /**
19990      * Clear all selections
19991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19992      */
19993     clearSelections : function(suppressEvent){
19994         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19995             this.cmp.elements = this.selections;
19996             this.cmp.removeClass(this.selectedClass);
19997             this.selections = [];
19998             if(!suppressEvent){
19999                 this.fireEvent("selectionchange", this, this.selections);
20000             }
20001         }
20002     },
20003
20004     /**
20005      * Returns true if the passed node is selected
20006      * @param {HTMLElement/Number} node The node or node index
20007      * @return {Boolean}
20008      */
20009     isSelected : function(node){
20010         var s = this.selections;
20011         if(s.length < 1){
20012             return false;
20013         }
20014         node = this.getNode(node);
20015         return s.indexOf(node) !== -1;
20016     },
20017
20018     /**
20019      * Selects nodes.
20020      * @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
20021      * @param {Boolean} keepExisting (optional) true to keep existing selections
20022      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20023      */
20024     select : function(nodeInfo, keepExisting, suppressEvent){
20025         if(nodeInfo instanceof Array){
20026             if(!keepExisting){
20027                 this.clearSelections(true);
20028             }
20029             for(var i = 0, len = nodeInfo.length; i < len; i++){
20030                 this.select(nodeInfo[i], true, true);
20031             }
20032             return;
20033         } 
20034         var node = this.getNode(nodeInfo);
20035         if(!node || this.isSelected(node)){
20036             return; // already selected.
20037         }
20038         if(!keepExisting){
20039             this.clearSelections(true);
20040         }
20041         
20042         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20043             Roo.fly(node).addClass(this.selectedClass);
20044             this.selections.push(node);
20045             if(!suppressEvent){
20046                 this.fireEvent("selectionchange", this, this.selections);
20047             }
20048         }
20049         
20050         
20051     },
20052       /**
20053      * Unselects nodes.
20054      * @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
20055      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20056      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20057      */
20058     unselect : function(nodeInfo, keepExisting, suppressEvent)
20059     {
20060         if(nodeInfo instanceof Array){
20061             Roo.each(this.selections, function(s) {
20062                 this.unselect(s, nodeInfo);
20063             }, this);
20064             return;
20065         }
20066         var node = this.getNode(nodeInfo);
20067         if(!node || !this.isSelected(node)){
20068             //Roo.log("not selected");
20069             return; // not selected.
20070         }
20071         // fireevent???
20072         var ns = [];
20073         Roo.each(this.selections, function(s) {
20074             if (s == node ) {
20075                 Roo.fly(node).removeClass(this.selectedClass);
20076
20077                 return;
20078             }
20079             ns.push(s);
20080         },this);
20081         
20082         this.selections= ns;
20083         this.fireEvent("selectionchange", this, this.selections);
20084     },
20085
20086     /**
20087      * Gets a template node.
20088      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20089      * @return {HTMLElement} The node or null if it wasn't found
20090      */
20091     getNode : function(nodeInfo){
20092         if(typeof nodeInfo == "string"){
20093             return document.getElementById(nodeInfo);
20094         }else if(typeof nodeInfo == "number"){
20095             return this.nodes[nodeInfo];
20096         }
20097         return nodeInfo;
20098     },
20099
20100     /**
20101      * Gets a range template nodes.
20102      * @param {Number} startIndex
20103      * @param {Number} endIndex
20104      * @return {Array} An array of nodes
20105      */
20106     getNodes : function(start, end){
20107         var ns = this.nodes;
20108         start = start || 0;
20109         end = typeof end == "undefined" ? ns.length - 1 : end;
20110         var nodes = [];
20111         if(start <= end){
20112             for(var i = start; i <= end; i++){
20113                 nodes.push(ns[i]);
20114             }
20115         } else{
20116             for(var i = start; i >= end; i--){
20117                 nodes.push(ns[i]);
20118             }
20119         }
20120         return nodes;
20121     },
20122
20123     /**
20124      * Finds the index of the passed node
20125      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20126      * @return {Number} The index of the node or -1
20127      */
20128     indexOf : function(node){
20129         node = this.getNode(node);
20130         if(typeof node.nodeIndex == "number"){
20131             return node.nodeIndex;
20132         }
20133         var ns = this.nodes;
20134         for(var i = 0, len = ns.length; i < len; i++){
20135             if(ns[i] == node){
20136                 return i;
20137             }
20138         }
20139         return -1;
20140     }
20141 });
20142 /*
20143  * - LGPL
20144  *
20145  * based on jquery fullcalendar
20146  * 
20147  */
20148
20149 Roo.bootstrap = Roo.bootstrap || {};
20150 /**
20151  * @class Roo.bootstrap.Calendar
20152  * @extends Roo.bootstrap.Component
20153  * Bootstrap Calendar class
20154  * @cfg {Boolean} loadMask (true|false) default false
20155  * @cfg {Object} header generate the user specific header of the calendar, default false
20156
20157  * @constructor
20158  * Create a new Container
20159  * @param {Object} config The config object
20160  */
20161
20162
20163
20164 Roo.bootstrap.Calendar = function(config){
20165     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20166      this.addEvents({
20167         /**
20168              * @event select
20169              * Fires when a date is selected
20170              * @param {DatePicker} this
20171              * @param {Date} date The selected date
20172              */
20173         'select': true,
20174         /**
20175              * @event monthchange
20176              * Fires when the displayed month changes 
20177              * @param {DatePicker} this
20178              * @param {Date} date The selected month
20179              */
20180         'monthchange': true,
20181         /**
20182              * @event evententer
20183              * Fires when mouse over an event
20184              * @param {Calendar} this
20185              * @param {event} Event
20186              */
20187         'evententer': true,
20188         /**
20189              * @event eventleave
20190              * Fires when the mouse leaves an
20191              * @param {Calendar} this
20192              * @param {event}
20193              */
20194         'eventleave': true,
20195         /**
20196              * @event eventclick
20197              * Fires when the mouse click an
20198              * @param {Calendar} this
20199              * @param {event}
20200              */
20201         'eventclick': true
20202         
20203     });
20204
20205 };
20206
20207 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20208     
20209           /**
20210      * @cfg {Roo.data.Store} store
20211      * The data source for the calendar
20212      */
20213         store : false,
20214      /**
20215      * @cfg {Number} startDay
20216      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20217      */
20218     startDay : 0,
20219     
20220     loadMask : false,
20221     
20222     header : false,
20223       
20224     getAutoCreate : function(){
20225         
20226         
20227         var fc_button = function(name, corner, style, content ) {
20228             return Roo.apply({},{
20229                 tag : 'span',
20230                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20231                          (corner.length ?
20232                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20233                             ''
20234                         ),
20235                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20236                 unselectable: 'on'
20237             });
20238         };
20239         
20240         var header = {};
20241         
20242         if(!this.header){
20243             header = {
20244                 tag : 'table',
20245                 cls : 'fc-header',
20246                 style : 'width:100%',
20247                 cn : [
20248                     {
20249                         tag: 'tr',
20250                         cn : [
20251                             {
20252                                 tag : 'td',
20253                                 cls : 'fc-header-left',
20254                                 cn : [
20255                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20256                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20257                                     { tag: 'span', cls: 'fc-header-space' },
20258                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20259
20260
20261                                 ]
20262                             },
20263
20264                             {
20265                                 tag : 'td',
20266                                 cls : 'fc-header-center',
20267                                 cn : [
20268                                     {
20269                                         tag: 'span',
20270                                         cls: 'fc-header-title',
20271                                         cn : {
20272                                             tag: 'H2',
20273                                             html : 'month / year'
20274                                         }
20275                                     }
20276
20277                                 ]
20278                             },
20279                             {
20280                                 tag : 'td',
20281                                 cls : 'fc-header-right',
20282                                 cn : [
20283                               /*      fc_button('month', 'left', '', 'month' ),
20284                                     fc_button('week', '', '', 'week' ),
20285                                     fc_button('day', 'right', '', 'day' )
20286                                 */    
20287
20288                                 ]
20289                             }
20290
20291                         ]
20292                     }
20293                 ]
20294             };
20295         }
20296         
20297         header = this.header;
20298         
20299        
20300         var cal_heads = function() {
20301             var ret = [];
20302             // fixme - handle this.
20303             
20304             for (var i =0; i < Date.dayNames.length; i++) {
20305                 var d = Date.dayNames[i];
20306                 ret.push({
20307                     tag: 'th',
20308                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20309                     html : d.substring(0,3)
20310                 });
20311                 
20312             }
20313             ret[0].cls += ' fc-first';
20314             ret[6].cls += ' fc-last';
20315             return ret;
20316         };
20317         var cal_cell = function(n) {
20318             return  {
20319                 tag: 'td',
20320                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20321                 cn : [
20322                     {
20323                         cn : [
20324                             {
20325                                 cls: 'fc-day-number',
20326                                 html: 'D'
20327                             },
20328                             {
20329                                 cls: 'fc-day-content',
20330                              
20331                                 cn : [
20332                                      {
20333                                         style: 'position: relative;' // height: 17px;
20334                                     }
20335                                 ]
20336                             }
20337                             
20338                             
20339                         ]
20340                     }
20341                 ]
20342                 
20343             }
20344         };
20345         var cal_rows = function() {
20346             
20347             var ret = [];
20348             for (var r = 0; r < 6; r++) {
20349                 var row= {
20350                     tag : 'tr',
20351                     cls : 'fc-week',
20352                     cn : []
20353                 };
20354                 
20355                 for (var i =0; i < Date.dayNames.length; i++) {
20356                     var d = Date.dayNames[i];
20357                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20358
20359                 }
20360                 row.cn[0].cls+=' fc-first';
20361                 row.cn[0].cn[0].style = 'min-height:90px';
20362                 row.cn[6].cls+=' fc-last';
20363                 ret.push(row);
20364                 
20365             }
20366             ret[0].cls += ' fc-first';
20367             ret[4].cls += ' fc-prev-last';
20368             ret[5].cls += ' fc-last';
20369             return ret;
20370             
20371         };
20372         
20373         var cal_table = {
20374             tag: 'table',
20375             cls: 'fc-border-separate',
20376             style : 'width:100%',
20377             cellspacing  : 0,
20378             cn : [
20379                 { 
20380                     tag: 'thead',
20381                     cn : [
20382                         { 
20383                             tag: 'tr',
20384                             cls : 'fc-first fc-last',
20385                             cn : cal_heads()
20386                         }
20387                     ]
20388                 },
20389                 { 
20390                     tag: 'tbody',
20391                     cn : cal_rows()
20392                 }
20393                   
20394             ]
20395         };
20396          
20397          var cfg = {
20398             cls : 'fc fc-ltr',
20399             cn : [
20400                 header,
20401                 {
20402                     cls : 'fc-content',
20403                     style : "position: relative;",
20404                     cn : [
20405                         {
20406                             cls : 'fc-view fc-view-month fc-grid',
20407                             style : 'position: relative',
20408                             unselectable : 'on',
20409                             cn : [
20410                                 {
20411                                     cls : 'fc-event-container',
20412                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20413                                 },
20414                                 cal_table
20415                             ]
20416                         }
20417                     ]
20418     
20419                 }
20420            ] 
20421             
20422         };
20423         
20424          
20425         
20426         return cfg;
20427     },
20428     
20429     
20430     initEvents : function()
20431     {
20432         if(!this.store){
20433             throw "can not find store for calendar";
20434         }
20435         
20436         var mark = {
20437             tag: "div",
20438             cls:"x-dlg-mask",
20439             style: "text-align:center",
20440             cn: [
20441                 {
20442                     tag: "div",
20443                     style: "background-color:white;width:50%;margin:250 auto",
20444                     cn: [
20445                         {
20446                             tag: "img",
20447                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20448                         },
20449                         {
20450                             tag: "span",
20451                             html: "Loading"
20452                         }
20453                         
20454                     ]
20455                 }
20456             ]
20457         };
20458         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20459         
20460         var size = this.el.select('.fc-content', true).first().getSize();
20461         this.maskEl.setSize(size.width, size.height);
20462         this.maskEl.enableDisplayMode("block");
20463         if(!this.loadMask){
20464             this.maskEl.hide();
20465         }
20466         
20467         this.store = Roo.factory(this.store, Roo.data);
20468         this.store.on('load', this.onLoad, this);
20469         this.store.on('beforeload', this.onBeforeLoad, this);
20470         
20471         this.resize();
20472         
20473         this.cells = this.el.select('.fc-day',true);
20474         //Roo.log(this.cells);
20475         this.textNodes = this.el.query('.fc-day-number');
20476         this.cells.addClassOnOver('fc-state-hover');
20477         
20478         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20479         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20480         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20481         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20482         
20483         this.on('monthchange', this.onMonthChange, this);
20484         
20485         this.update(new Date().clearTime());
20486     },
20487     
20488     resize : function() {
20489         var sz  = this.el.getSize();
20490         
20491         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20492         this.el.select('.fc-day-content div',true).setHeight(34);
20493     },
20494     
20495     
20496     // private
20497     showPrevMonth : function(e){
20498         this.update(this.activeDate.add("mo", -1));
20499     },
20500     showToday : function(e){
20501         this.update(new Date().clearTime());
20502     },
20503     // private
20504     showNextMonth : function(e){
20505         this.update(this.activeDate.add("mo", 1));
20506     },
20507
20508     // private
20509     showPrevYear : function(){
20510         this.update(this.activeDate.add("y", -1));
20511     },
20512
20513     // private
20514     showNextYear : function(){
20515         this.update(this.activeDate.add("y", 1));
20516     },
20517
20518     
20519    // private
20520     update : function(date)
20521     {
20522         var vd = this.activeDate;
20523         this.activeDate = date;
20524 //        if(vd && this.el){
20525 //            var t = date.getTime();
20526 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20527 //                Roo.log('using add remove');
20528 //                
20529 //                this.fireEvent('monthchange', this, date);
20530 //                
20531 //                this.cells.removeClass("fc-state-highlight");
20532 //                this.cells.each(function(c){
20533 //                   if(c.dateValue == t){
20534 //                       c.addClass("fc-state-highlight");
20535 //                       setTimeout(function(){
20536 //                            try{c.dom.firstChild.focus();}catch(e){}
20537 //                       }, 50);
20538 //                       return false;
20539 //                   }
20540 //                   return true;
20541 //                });
20542 //                return;
20543 //            }
20544 //        }
20545         
20546         var days = date.getDaysInMonth();
20547         
20548         var firstOfMonth = date.getFirstDateOfMonth();
20549         var startingPos = firstOfMonth.getDay()-this.startDay;
20550         
20551         if(startingPos < this.startDay){
20552             startingPos += 7;
20553         }
20554         
20555         var pm = date.add(Date.MONTH, -1);
20556         var prevStart = pm.getDaysInMonth()-startingPos;
20557 //        
20558         this.cells = this.el.select('.fc-day',true);
20559         this.textNodes = this.el.query('.fc-day-number');
20560         this.cells.addClassOnOver('fc-state-hover');
20561         
20562         var cells = this.cells.elements;
20563         var textEls = this.textNodes;
20564         
20565         Roo.each(cells, function(cell){
20566             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20567         });
20568         
20569         days += startingPos;
20570
20571         // convert everything to numbers so it's fast
20572         var day = 86400000;
20573         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20574         //Roo.log(d);
20575         //Roo.log(pm);
20576         //Roo.log(prevStart);
20577         
20578         var today = new Date().clearTime().getTime();
20579         var sel = date.clearTime().getTime();
20580         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20581         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20582         var ddMatch = this.disabledDatesRE;
20583         var ddText = this.disabledDatesText;
20584         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20585         var ddaysText = this.disabledDaysText;
20586         var format = this.format;
20587         
20588         var setCellClass = function(cal, cell){
20589             cell.row = 0;
20590             cell.events = [];
20591             cell.more = [];
20592             //Roo.log('set Cell Class');
20593             cell.title = "";
20594             var t = d.getTime();
20595             
20596             //Roo.log(d);
20597             
20598             cell.dateValue = t;
20599             if(t == today){
20600                 cell.className += " fc-today";
20601                 cell.className += " fc-state-highlight";
20602                 cell.title = cal.todayText;
20603             }
20604             if(t == sel){
20605                 // disable highlight in other month..
20606                 //cell.className += " fc-state-highlight";
20607                 
20608             }
20609             // disabling
20610             if(t < min) {
20611                 cell.className = " fc-state-disabled";
20612                 cell.title = cal.minText;
20613                 return;
20614             }
20615             if(t > max) {
20616                 cell.className = " fc-state-disabled";
20617                 cell.title = cal.maxText;
20618                 return;
20619             }
20620             if(ddays){
20621                 if(ddays.indexOf(d.getDay()) != -1){
20622                     cell.title = ddaysText;
20623                     cell.className = " fc-state-disabled";
20624                 }
20625             }
20626             if(ddMatch && format){
20627                 var fvalue = d.dateFormat(format);
20628                 if(ddMatch.test(fvalue)){
20629                     cell.title = ddText.replace("%0", fvalue);
20630                     cell.className = " fc-state-disabled";
20631                 }
20632             }
20633             
20634             if (!cell.initialClassName) {
20635                 cell.initialClassName = cell.dom.className;
20636             }
20637             
20638             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20639         };
20640
20641         var i = 0;
20642         
20643         for(; i < startingPos; i++) {
20644             textEls[i].innerHTML = (++prevStart);
20645             d.setDate(d.getDate()+1);
20646             
20647             cells[i].className = "fc-past fc-other-month";
20648             setCellClass(this, cells[i]);
20649         }
20650         
20651         var intDay = 0;
20652         
20653         for(; i < days; i++){
20654             intDay = i - startingPos + 1;
20655             textEls[i].innerHTML = (intDay);
20656             d.setDate(d.getDate()+1);
20657             
20658             cells[i].className = ''; // "x-date-active";
20659             setCellClass(this, cells[i]);
20660         }
20661         var extraDays = 0;
20662         
20663         for(; i < 42; i++) {
20664             textEls[i].innerHTML = (++extraDays);
20665             d.setDate(d.getDate()+1);
20666             
20667             cells[i].className = "fc-future fc-other-month";
20668             setCellClass(this, cells[i]);
20669         }
20670         
20671         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20672         
20673         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20674         
20675         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20676         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20677         
20678         if(totalRows != 6){
20679             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20680             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20681         }
20682         
20683         this.fireEvent('monthchange', this, date);
20684         
20685         
20686         /*
20687         if(!this.internalRender){
20688             var main = this.el.dom.firstChild;
20689             var w = main.offsetWidth;
20690             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20691             Roo.fly(main).setWidth(w);
20692             this.internalRender = true;
20693             // opera does not respect the auto grow header center column
20694             // then, after it gets a width opera refuses to recalculate
20695             // without a second pass
20696             if(Roo.isOpera && !this.secondPass){
20697                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20698                 this.secondPass = true;
20699                 this.update.defer(10, this, [date]);
20700             }
20701         }
20702         */
20703         
20704     },
20705     
20706     findCell : function(dt) {
20707         dt = dt.clearTime().getTime();
20708         var ret = false;
20709         this.cells.each(function(c){
20710             //Roo.log("check " +c.dateValue + '?=' + dt);
20711             if(c.dateValue == dt){
20712                 ret = c;
20713                 return false;
20714             }
20715             return true;
20716         });
20717         
20718         return ret;
20719     },
20720     
20721     findCells : function(ev) {
20722         var s = ev.start.clone().clearTime().getTime();
20723        // Roo.log(s);
20724         var e= ev.end.clone().clearTime().getTime();
20725        // Roo.log(e);
20726         var ret = [];
20727         this.cells.each(function(c){
20728              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20729             
20730             if(c.dateValue > e){
20731                 return ;
20732             }
20733             if(c.dateValue < s){
20734                 return ;
20735             }
20736             ret.push(c);
20737         });
20738         
20739         return ret;    
20740     },
20741     
20742 //    findBestRow: function(cells)
20743 //    {
20744 //        var ret = 0;
20745 //        
20746 //        for (var i =0 ; i < cells.length;i++) {
20747 //            ret  = Math.max(cells[i].rows || 0,ret);
20748 //        }
20749 //        return ret;
20750 //        
20751 //    },
20752     
20753     
20754     addItem : function(ev)
20755     {
20756         // look for vertical location slot in
20757         var cells = this.findCells(ev);
20758         
20759 //        ev.row = this.findBestRow(cells);
20760         
20761         // work out the location.
20762         
20763         var crow = false;
20764         var rows = [];
20765         for(var i =0; i < cells.length; i++) {
20766             
20767             cells[i].row = cells[0].row;
20768             
20769             if(i == 0){
20770                 cells[i].row = cells[i].row + 1;
20771             }
20772             
20773             if (!crow) {
20774                 crow = {
20775                     start : cells[i],
20776                     end :  cells[i]
20777                 };
20778                 continue;
20779             }
20780             if (crow.start.getY() == cells[i].getY()) {
20781                 // on same row.
20782                 crow.end = cells[i];
20783                 continue;
20784             }
20785             // different row.
20786             rows.push(crow);
20787             crow = {
20788                 start: cells[i],
20789                 end : cells[i]
20790             };
20791             
20792         }
20793         
20794         rows.push(crow);
20795         ev.els = [];
20796         ev.rows = rows;
20797         ev.cells = cells;
20798         
20799         cells[0].events.push(ev);
20800         
20801         this.calevents.push(ev);
20802     },
20803     
20804     clearEvents: function() {
20805         
20806         if(!this.calevents){
20807             return;
20808         }
20809         
20810         Roo.each(this.cells.elements, function(c){
20811             c.row = 0;
20812             c.events = [];
20813             c.more = [];
20814         });
20815         
20816         Roo.each(this.calevents, function(e) {
20817             Roo.each(e.els, function(el) {
20818                 el.un('mouseenter' ,this.onEventEnter, this);
20819                 el.un('mouseleave' ,this.onEventLeave, this);
20820                 el.remove();
20821             },this);
20822         },this);
20823         
20824         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20825             e.remove();
20826         });
20827         
20828     },
20829     
20830     renderEvents: function()
20831     {   
20832         var _this = this;
20833         
20834         this.cells.each(function(c) {
20835             
20836             if(c.row < 5){
20837                 return;
20838             }
20839             
20840             var ev = c.events;
20841             
20842             var r = 4;
20843             if(c.row != c.events.length){
20844                 r = 4 - (4 - (c.row - c.events.length));
20845             }
20846             
20847             c.events = ev.slice(0, r);
20848             c.more = ev.slice(r);
20849             
20850             if(c.more.length && c.more.length == 1){
20851                 c.events.push(c.more.pop());
20852             }
20853             
20854             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20855             
20856         });
20857             
20858         this.cells.each(function(c) {
20859             
20860             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20861             
20862             
20863             for (var e = 0; e < c.events.length; e++){
20864                 var ev = c.events[e];
20865                 var rows = ev.rows;
20866                 
20867                 for(var i = 0; i < rows.length; i++) {
20868                 
20869                     // how many rows should it span..
20870
20871                     var  cfg = {
20872                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20873                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20874
20875                         unselectable : "on",
20876                         cn : [
20877                             {
20878                                 cls: 'fc-event-inner',
20879                                 cn : [
20880     //                                {
20881     //                                  tag:'span',
20882     //                                  cls: 'fc-event-time',
20883     //                                  html : cells.length > 1 ? '' : ev.time
20884     //                                },
20885                                     {
20886                                       tag:'span',
20887                                       cls: 'fc-event-title',
20888                                       html : String.format('{0}', ev.title)
20889                                     }
20890
20891
20892                                 ]
20893                             },
20894                             {
20895                                 cls: 'ui-resizable-handle ui-resizable-e',
20896                                 html : '&nbsp;&nbsp;&nbsp'
20897                             }
20898
20899                         ]
20900                     };
20901
20902                     if (i == 0) {
20903                         cfg.cls += ' fc-event-start';
20904                     }
20905                     if ((i+1) == rows.length) {
20906                         cfg.cls += ' fc-event-end';
20907                     }
20908
20909                     var ctr = _this.el.select('.fc-event-container',true).first();
20910                     var cg = ctr.createChild(cfg);
20911
20912                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20913                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20914
20915                     var r = (c.more.length) ? 1 : 0;
20916                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20917                     cg.setWidth(ebox.right - sbox.x -2);
20918
20919                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20920                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20921                     cg.on('click', _this.onEventClick, _this, ev);
20922
20923                     ev.els.push(cg);
20924                     
20925                 }
20926                 
20927             }
20928             
20929             
20930             if(c.more.length){
20931                 var  cfg = {
20932                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20933                     style : 'position: absolute',
20934                     unselectable : "on",
20935                     cn : [
20936                         {
20937                             cls: 'fc-event-inner',
20938                             cn : [
20939                                 {
20940                                   tag:'span',
20941                                   cls: 'fc-event-title',
20942                                   html : 'More'
20943                                 }
20944
20945
20946                             ]
20947                         },
20948                         {
20949                             cls: 'ui-resizable-handle ui-resizable-e',
20950                             html : '&nbsp;&nbsp;&nbsp'
20951                         }
20952
20953                     ]
20954                 };
20955
20956                 var ctr = _this.el.select('.fc-event-container',true).first();
20957                 var cg = ctr.createChild(cfg);
20958
20959                 var sbox = c.select('.fc-day-content',true).first().getBox();
20960                 var ebox = c.select('.fc-day-content',true).first().getBox();
20961                 //Roo.log(cg);
20962                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20963                 cg.setWidth(ebox.right - sbox.x -2);
20964
20965                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20966                 
20967             }
20968             
20969         });
20970         
20971         
20972         
20973     },
20974     
20975     onEventEnter: function (e, el,event,d) {
20976         this.fireEvent('evententer', this, el, event);
20977     },
20978     
20979     onEventLeave: function (e, el,event,d) {
20980         this.fireEvent('eventleave', this, el, event);
20981     },
20982     
20983     onEventClick: function (e, el,event,d) {
20984         this.fireEvent('eventclick', this, el, event);
20985     },
20986     
20987     onMonthChange: function () {
20988         this.store.load();
20989     },
20990     
20991     onMoreEventClick: function(e, el, more)
20992     {
20993         var _this = this;
20994         
20995         this.calpopover.placement = 'right';
20996         this.calpopover.setTitle('More');
20997         
20998         this.calpopover.setContent('');
20999         
21000         var ctr = this.calpopover.el.select('.popover-content', true).first();
21001         
21002         Roo.each(more, function(m){
21003             var cfg = {
21004                 cls : 'fc-event-hori fc-event-draggable',
21005                 html : m.title
21006             };
21007             var cg = ctr.createChild(cfg);
21008             
21009             cg.on('click', _this.onEventClick, _this, m);
21010         });
21011         
21012         this.calpopover.show(el);
21013         
21014         
21015     },
21016     
21017     onLoad: function () 
21018     {   
21019         this.calevents = [];
21020         var cal = this;
21021         
21022         if(this.store.getCount() > 0){
21023             this.store.data.each(function(d){
21024                cal.addItem({
21025                     id : d.data.id,
21026                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21027                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21028                     time : d.data.start_time,
21029                     title : d.data.title,
21030                     description : d.data.description,
21031                     venue : d.data.venue
21032                 });
21033             });
21034         }
21035         
21036         this.renderEvents();
21037         
21038         if(this.calevents.length && this.loadMask){
21039             this.maskEl.hide();
21040         }
21041     },
21042     
21043     onBeforeLoad: function()
21044     {
21045         this.clearEvents();
21046         if(this.loadMask){
21047             this.maskEl.show();
21048         }
21049     }
21050 });
21051
21052  
21053  /*
21054  * - LGPL
21055  *
21056  * element
21057  * 
21058  */
21059
21060 /**
21061  * @class Roo.bootstrap.Popover
21062  * @extends Roo.bootstrap.Component
21063  * Bootstrap Popover class
21064  * @cfg {String} html contents of the popover   (or false to use children..)
21065  * @cfg {String} title of popover (or false to hide)
21066  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21067  * @cfg {String} trigger click || hover (or false to trigger manually)
21068  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21069  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21070  *      - if false and it has a 'parent' then it will be automatically added to that element
21071  *      - if string - Roo.get  will be called 
21072  * @cfg {Number} delay - delay before showing
21073  
21074  * @constructor
21075  * Create a new Popover
21076  * @param {Object} config The config object
21077  */
21078
21079 Roo.bootstrap.Popover = function(config){
21080     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21081     
21082     this.addEvents({
21083         // raw events
21084          /**
21085          * @event show
21086          * After the popover show
21087          * 
21088          * @param {Roo.bootstrap.Popover} this
21089          */
21090         "show" : true,
21091         /**
21092          * @event hide
21093          * After the popover hide
21094          * 
21095          * @param {Roo.bootstrap.Popover} this
21096          */
21097         "hide" : true
21098     });
21099 };
21100
21101 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21102     
21103     title: false,
21104     html: false,
21105     
21106     placement : 'right',
21107     trigger : 'hover', // hover
21108     modal : false,
21109     delay : 0,
21110     
21111     over: false,
21112     
21113     can_build_overlaid : false,
21114     
21115     maskEl : false, // the mask element
21116     headerEl : false,
21117     contentEl : false,
21118     alignEl : false, // when show is called with an element - this get's stored.
21119     
21120     getChildContainer : function()
21121     {
21122         return this.contentEl;
21123         
21124     },
21125     getPopoverHeader : function()
21126     {
21127         this.title = true; // flag not to hide it..
21128         this.headerEl.addClass('p-0');
21129         return this.headerEl
21130     },
21131     
21132     
21133     getAutoCreate : function(){
21134          
21135         var cfg = {
21136            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21137            style: 'display:block',
21138            cn : [
21139                 {
21140                     cls : 'arrow'
21141                 },
21142                 {
21143                     cls : 'popover-inner ',
21144                     cn : [
21145                         {
21146                             tag: 'h3',
21147                             cls: 'popover-title popover-header',
21148                             html : this.title === false ? '' : this.title
21149                         },
21150                         {
21151                             cls : 'popover-content popover-body '  + (this.cls || ''),
21152                             html : this.html || ''
21153                         }
21154                     ]
21155                     
21156                 }
21157            ]
21158         };
21159         
21160         return cfg;
21161     },
21162     /**
21163      * @param {string} the title
21164      */
21165     setTitle: function(str)
21166     {
21167         this.title = str;
21168         if (this.el) {
21169             this.headerEl.dom.innerHTML = str;
21170         }
21171         
21172     },
21173     /**
21174      * @param {string} the body content
21175      */
21176     setContent: function(str)
21177     {
21178         this.html = str;
21179         if (this.contentEl) {
21180             this.contentEl.dom.innerHTML = str;
21181         }
21182         
21183     },
21184     // as it get's added to the bottom of the page.
21185     onRender : function(ct, position)
21186     {
21187         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21188         
21189         
21190         
21191         if(!this.el){
21192             var cfg = Roo.apply({},  this.getAutoCreate());
21193             cfg.id = Roo.id();
21194             
21195             if (this.cls) {
21196                 cfg.cls += ' ' + this.cls;
21197             }
21198             if (this.style) {
21199                 cfg.style = this.style;
21200             }
21201             //Roo.log("adding to ");
21202             this.el = Roo.get(document.body).createChild(cfg, position);
21203 //            Roo.log(this.el);
21204         }
21205         
21206         this.contentEl = this.el.select('.popover-content',true).first();
21207         this.headerEl =  this.el.select('.popover-title',true).first();
21208         
21209         var nitems = [];
21210         if(typeof(this.items) != 'undefined'){
21211             var items = this.items;
21212             delete this.items;
21213
21214             for(var i =0;i < items.length;i++) {
21215                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21216             }
21217         }
21218
21219         this.items = nitems;
21220         
21221         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21222         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21223         
21224         
21225         
21226         this.initEvents();
21227     },
21228     
21229     resizeMask : function()
21230     {
21231         this.maskEl.setSize(
21232             Roo.lib.Dom.getViewWidth(true),
21233             Roo.lib.Dom.getViewHeight(true)
21234         );
21235     },
21236     
21237     initEvents : function()
21238     {
21239         
21240         if (!this.modal) { 
21241             Roo.bootstrap.Popover.register(this);
21242         }
21243          
21244         this.arrowEl = this.el.select('.arrow',true).first();
21245         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21246         this.el.enableDisplayMode('block');
21247         this.el.hide();
21248  
21249         
21250         if (this.over === false && !this.parent()) {
21251             return; 
21252         }
21253         if (this.triggers === false) {
21254             return;
21255         }
21256          
21257         // support parent
21258         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21259         var triggers = this.trigger ? this.trigger.split(' ') : [];
21260         Roo.each(triggers, function(trigger) {
21261         
21262             if (trigger == 'click') {
21263                 on_el.on('click', this.toggle, this);
21264             } else if (trigger != 'manual') {
21265                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21266                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21267       
21268                 on_el.on(eventIn  ,this.enter, this);
21269                 on_el.on(eventOut, this.leave, this);
21270             }
21271         }, this);
21272     },
21273     
21274     
21275     // private
21276     timeout : null,
21277     hoverState : null,
21278     
21279     toggle : function () {
21280         this.hoverState == 'in' ? this.leave() : this.enter();
21281     },
21282     
21283     enter : function () {
21284         
21285         clearTimeout(this.timeout);
21286     
21287         this.hoverState = 'in';
21288     
21289         if (!this.delay || !this.delay.show) {
21290             this.show();
21291             return;
21292         }
21293         var _t = this;
21294         this.timeout = setTimeout(function () {
21295             if (_t.hoverState == 'in') {
21296                 _t.show();
21297             }
21298         }, this.delay.show)
21299     },
21300     
21301     leave : function() {
21302         clearTimeout(this.timeout);
21303     
21304         this.hoverState = 'out';
21305     
21306         if (!this.delay || !this.delay.hide) {
21307             this.hide();
21308             return;
21309         }
21310         var _t = this;
21311         this.timeout = setTimeout(function () {
21312             if (_t.hoverState == 'out') {
21313                 _t.hide();
21314             }
21315         }, this.delay.hide)
21316     },
21317     /**
21318      * Show the popover
21319      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21320      * @param {string} (left|right|top|bottom) position
21321      */
21322     show : function (on_el, placement)
21323     {
21324         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21325         on_el = on_el || false; // default to false
21326          
21327         if (!on_el) {
21328             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21329                 on_el = this.parent().el;
21330             } else if (this.over) {
21331                 on_el = Roo.get(this.over);
21332             }
21333             
21334         }
21335         
21336         this.alignEl = Roo.get( on_el );
21337
21338         if (!this.el) {
21339             this.render(document.body);
21340         }
21341         
21342         
21343          
21344         
21345         if (this.title === false) {
21346             this.headerEl.hide();
21347         }
21348         
21349        
21350         this.el.show();
21351         this.el.dom.style.display = 'block';
21352          
21353  
21354         if (this.alignEl) {
21355             this.updatePosition(this.placement, true);
21356              
21357         } else {
21358             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21359             var es = this.el.getSize();
21360             var x = Roo.lib.Dom.getViewWidth()/2;
21361             var y = Roo.lib.Dom.getViewHeight()/2;
21362             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21363             
21364         }
21365
21366         
21367         //var arrow = this.el.select('.arrow',true).first();
21368         //arrow.set(align[2], 
21369         
21370         this.el.addClass('in');
21371         
21372          
21373         
21374         this.hoverState = 'in';
21375         
21376         if (this.modal) {
21377             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21378             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21379             this.maskEl.dom.style.display = 'block';
21380             this.maskEl.addClass('show');
21381         }
21382         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21383  
21384         this.fireEvent('show', this);
21385         
21386     },
21387     /**
21388      * fire this manually after loading a grid in the table for example
21389      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21390      * @param {Boolean} try and move it if we cant get right position.
21391      */
21392     updatePosition : function(placement, try_move)
21393     {
21394         // allow for calling with no parameters
21395         placement = placement   ? placement :  this.placement;
21396         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21397         
21398         this.el.removeClass([
21399             'fade','top','bottom', 'left', 'right','in',
21400             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21401         ]);
21402         this.el.addClass(placement + ' bs-popover-' + placement);
21403         
21404         if (!this.alignEl ) {
21405             return false;
21406         }
21407         
21408         switch (placement) {
21409             case 'right':
21410                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21411                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21412                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21413                     //normal display... or moved up/down.
21414                     this.el.setXY(offset);
21415                     var xy = this.alignEl.getAnchorXY('tr', false);
21416                     xy[0]+=2;xy[1]+=5;
21417                     this.arrowEl.setXY(xy);
21418                     return true;
21419                 }
21420                 // continue through...
21421                 return this.updatePosition('left', false);
21422                 
21423             
21424             case 'left':
21425                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21426                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21427                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21428                     //normal display... or moved up/down.
21429                     this.el.setXY(offset);
21430                     var xy = this.alignEl.getAnchorXY('tl', false);
21431                     xy[0]-=10;xy[1]+=5; // << fix me
21432                     this.arrowEl.setXY(xy);
21433                     return true;
21434                 }
21435                 // call self...
21436                 return this.updatePosition('right', false);
21437             
21438             case 'top':
21439                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21440                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21441                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21442                     //normal display... or moved up/down.
21443                     this.el.setXY(offset);
21444                     var xy = this.alignEl.getAnchorXY('t', false);
21445                     xy[1]-=10; // << fix me
21446                     this.arrowEl.setXY(xy);
21447                     return true;
21448                 }
21449                 // fall through
21450                return this.updatePosition('bottom', false);
21451             
21452             case 'bottom':
21453                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21454                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21455                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21456                     //normal display... or moved up/down.
21457                     this.el.setXY(offset);
21458                     var xy = this.alignEl.getAnchorXY('b', false);
21459                      xy[1]+=2; // << fix me
21460                     this.arrowEl.setXY(xy);
21461                     return true;
21462                 }
21463                 // fall through
21464                 return this.updatePosition('top', false);
21465                 
21466             
21467         }
21468         
21469         
21470         return false;
21471     },
21472     
21473     hide : function()
21474     {
21475         this.el.setXY([0,0]);
21476         this.el.removeClass('in');
21477         this.el.hide();
21478         this.hoverState = null;
21479         this.maskEl.hide(); // always..
21480         this.fireEvent('hide', this);
21481     }
21482     
21483 });
21484
21485
21486 Roo.apply(Roo.bootstrap.Popover, {
21487
21488     alignment : {
21489         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21490         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21491         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21492         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21493     },
21494     
21495     zIndex : 20001,
21496
21497     clickHander : false,
21498     
21499     
21500
21501     onMouseDown : function(e)
21502     {
21503         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21504             /// what is nothing is showing..
21505             this.hideAll();
21506         }
21507          
21508     },
21509     
21510     
21511     popups : [],
21512     
21513     register : function(popup)
21514     {
21515         if (!Roo.bootstrap.Popover.clickHandler) {
21516             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21517         }
21518         // hide other popups.
21519         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21520         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21521         this.hideAll(); //<< why?
21522         //this.popups.push(popup);
21523     },
21524     hideAll : function()
21525     {
21526         this.popups.forEach(function(p) {
21527             p.hide();
21528         });
21529     },
21530     onShow : function() {
21531         Roo.bootstrap.Popover.popups.push(this);
21532     },
21533     onHide : function() {
21534         Roo.bootstrap.Popover.popups.remove(this);
21535     } 
21536
21537 });/*
21538  * - LGPL
21539  *
21540  * Card header - holder for the card header elements.
21541  * 
21542  */
21543
21544 /**
21545  * @class Roo.bootstrap.PopoverNav
21546  * @extends Roo.bootstrap.NavGroup
21547  * Bootstrap Popover header navigation class
21548  * @constructor
21549  * Create a new Popover Header Navigation 
21550  * @param {Object} config The config object
21551  */
21552
21553 Roo.bootstrap.PopoverNav = function(config){
21554     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21555 };
21556
21557 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21558     
21559     
21560     container_method : 'getPopoverHeader' 
21561     
21562      
21563     
21564     
21565    
21566 });
21567
21568  
21569
21570  /*
21571  * - LGPL
21572  *
21573  * Progress
21574  * 
21575  */
21576
21577 /**
21578  * @class Roo.bootstrap.Progress
21579  * @extends Roo.bootstrap.Component
21580  * Bootstrap Progress class
21581  * @cfg {Boolean} striped striped of the progress bar
21582  * @cfg {Boolean} active animated of the progress bar
21583  * 
21584  * 
21585  * @constructor
21586  * Create a new Progress
21587  * @param {Object} config The config object
21588  */
21589
21590 Roo.bootstrap.Progress = function(config){
21591     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21592 };
21593
21594 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21595     
21596     striped : false,
21597     active: false,
21598     
21599     getAutoCreate : function(){
21600         var cfg = {
21601             tag: 'div',
21602             cls: 'progress'
21603         };
21604         
21605         
21606         if(this.striped){
21607             cfg.cls += ' progress-striped';
21608         }
21609       
21610         if(this.active){
21611             cfg.cls += ' active';
21612         }
21613         
21614         
21615         return cfg;
21616     }
21617    
21618 });
21619
21620  
21621
21622  /*
21623  * - LGPL
21624  *
21625  * ProgressBar
21626  * 
21627  */
21628
21629 /**
21630  * @class Roo.bootstrap.ProgressBar
21631  * @extends Roo.bootstrap.Component
21632  * Bootstrap ProgressBar class
21633  * @cfg {Number} aria_valuenow aria-value now
21634  * @cfg {Number} aria_valuemin aria-value min
21635  * @cfg {Number} aria_valuemax aria-value max
21636  * @cfg {String} label label for the progress bar
21637  * @cfg {String} panel (success | info | warning | danger )
21638  * @cfg {String} role role of the progress bar
21639  * @cfg {String} sr_only text
21640  * 
21641  * 
21642  * @constructor
21643  * Create a new ProgressBar
21644  * @param {Object} config The config object
21645  */
21646
21647 Roo.bootstrap.ProgressBar = function(config){
21648     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21649 };
21650
21651 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21652     
21653     aria_valuenow : 0,
21654     aria_valuemin : 0,
21655     aria_valuemax : 100,
21656     label : false,
21657     panel : false,
21658     role : false,
21659     sr_only: false,
21660     
21661     getAutoCreate : function()
21662     {
21663         
21664         var cfg = {
21665             tag: 'div',
21666             cls: 'progress-bar',
21667             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21668         };
21669         
21670         if(this.sr_only){
21671             cfg.cn = {
21672                 tag: 'span',
21673                 cls: 'sr-only',
21674                 html: this.sr_only
21675             }
21676         }
21677         
21678         if(this.role){
21679             cfg.role = this.role;
21680         }
21681         
21682         if(this.aria_valuenow){
21683             cfg['aria-valuenow'] = this.aria_valuenow;
21684         }
21685         
21686         if(this.aria_valuemin){
21687             cfg['aria-valuemin'] = this.aria_valuemin;
21688         }
21689         
21690         if(this.aria_valuemax){
21691             cfg['aria-valuemax'] = this.aria_valuemax;
21692         }
21693         
21694         if(this.label && !this.sr_only){
21695             cfg.html = this.label;
21696         }
21697         
21698         if(this.panel){
21699             cfg.cls += ' progress-bar-' + this.panel;
21700         }
21701         
21702         return cfg;
21703     },
21704     
21705     update : function(aria_valuenow)
21706     {
21707         this.aria_valuenow = aria_valuenow;
21708         
21709         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21710     }
21711    
21712 });
21713
21714  
21715
21716  /*
21717  * - LGPL
21718  *
21719  * column
21720  * 
21721  */
21722
21723 /**
21724  * @class Roo.bootstrap.TabGroup
21725  * @extends Roo.bootstrap.Column
21726  * Bootstrap Column class
21727  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21728  * @cfg {Boolean} carousel true to make the group behave like a carousel
21729  * @cfg {Boolean} bullets show bullets for the panels
21730  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21731  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21732  * @cfg {Boolean} showarrow (true|false) show arrow default true
21733  * 
21734  * @constructor
21735  * Create a new TabGroup
21736  * @param {Object} config The config object
21737  */
21738
21739 Roo.bootstrap.TabGroup = function(config){
21740     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21741     if (!this.navId) {
21742         this.navId = Roo.id();
21743     }
21744     this.tabs = [];
21745     Roo.bootstrap.TabGroup.register(this);
21746     
21747 };
21748
21749 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21750     
21751     carousel : false,
21752     transition : false,
21753     bullets : 0,
21754     timer : 0,
21755     autoslide : false,
21756     slideFn : false,
21757     slideOnTouch : false,
21758     showarrow : true,
21759     
21760     getAutoCreate : function()
21761     {
21762         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21763         
21764         cfg.cls += ' tab-content';
21765         
21766         if (this.carousel) {
21767             cfg.cls += ' carousel slide';
21768             
21769             cfg.cn = [{
21770                cls : 'carousel-inner',
21771                cn : []
21772             }];
21773         
21774             if(this.bullets  && !Roo.isTouch){
21775                 
21776                 var bullets = {
21777                     cls : 'carousel-bullets',
21778                     cn : []
21779                 };
21780                
21781                 if(this.bullets_cls){
21782                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21783                 }
21784                 
21785                 bullets.cn.push({
21786                     cls : 'clear'
21787                 });
21788                 
21789                 cfg.cn[0].cn.push(bullets);
21790             }
21791             
21792             if(this.showarrow){
21793                 cfg.cn[0].cn.push({
21794                     tag : 'div',
21795                     class : 'carousel-arrow',
21796                     cn : [
21797                         {
21798                             tag : 'div',
21799                             class : 'carousel-prev',
21800                             cn : [
21801                                 {
21802                                     tag : 'i',
21803                                     class : 'fa fa-chevron-left'
21804                                 }
21805                             ]
21806                         },
21807                         {
21808                             tag : 'div',
21809                             class : 'carousel-next',
21810                             cn : [
21811                                 {
21812                                     tag : 'i',
21813                                     class : 'fa fa-chevron-right'
21814                                 }
21815                             ]
21816                         }
21817                     ]
21818                 });
21819             }
21820             
21821         }
21822         
21823         return cfg;
21824     },
21825     
21826     initEvents:  function()
21827     {
21828 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21829 //            this.el.on("touchstart", this.onTouchStart, this);
21830 //        }
21831         
21832         if(this.autoslide){
21833             var _this = this;
21834             
21835             this.slideFn = window.setInterval(function() {
21836                 _this.showPanelNext();
21837             }, this.timer);
21838         }
21839         
21840         if(this.showarrow){
21841             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21842             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21843         }
21844         
21845         
21846     },
21847     
21848 //    onTouchStart : function(e, el, o)
21849 //    {
21850 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21851 //            return;
21852 //        }
21853 //        
21854 //        this.showPanelNext();
21855 //    },
21856     
21857     
21858     getChildContainer : function()
21859     {
21860         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21861     },
21862     
21863     /**
21864     * register a Navigation item
21865     * @param {Roo.bootstrap.NavItem} the navitem to add
21866     */
21867     register : function(item)
21868     {
21869         this.tabs.push( item);
21870         item.navId = this.navId; // not really needed..
21871         this.addBullet();
21872     
21873     },
21874     
21875     getActivePanel : function()
21876     {
21877         var r = false;
21878         Roo.each(this.tabs, function(t) {
21879             if (t.active) {
21880                 r = t;
21881                 return false;
21882             }
21883             return null;
21884         });
21885         return r;
21886         
21887     },
21888     getPanelByName : function(n)
21889     {
21890         var r = false;
21891         Roo.each(this.tabs, function(t) {
21892             if (t.tabId == n) {
21893                 r = t;
21894                 return false;
21895             }
21896             return null;
21897         });
21898         return r;
21899     },
21900     indexOfPanel : function(p)
21901     {
21902         var r = false;
21903         Roo.each(this.tabs, function(t,i) {
21904             if (t.tabId == p.tabId) {
21905                 r = i;
21906                 return false;
21907             }
21908             return null;
21909         });
21910         return r;
21911     },
21912     /**
21913      * show a specific panel
21914      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21915      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21916      */
21917     showPanel : function (pan)
21918     {
21919         if(this.transition || typeof(pan) == 'undefined'){
21920             Roo.log("waiting for the transitionend");
21921             return false;
21922         }
21923         
21924         if (typeof(pan) == 'number') {
21925             pan = this.tabs[pan];
21926         }
21927         
21928         if (typeof(pan) == 'string') {
21929             pan = this.getPanelByName(pan);
21930         }
21931         
21932         var cur = this.getActivePanel();
21933         
21934         if(!pan || !cur){
21935             Roo.log('pan or acitve pan is undefined');
21936             return false;
21937         }
21938         
21939         if (pan.tabId == this.getActivePanel().tabId) {
21940             return true;
21941         }
21942         
21943         if (false === cur.fireEvent('beforedeactivate')) {
21944             return false;
21945         }
21946         
21947         if(this.bullets > 0 && !Roo.isTouch){
21948             this.setActiveBullet(this.indexOfPanel(pan));
21949         }
21950         
21951         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21952             
21953             //class="carousel-item carousel-item-next carousel-item-left"
21954             
21955             this.transition = true;
21956             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21957             var lr = dir == 'next' ? 'left' : 'right';
21958             pan.el.addClass(dir); // or prev
21959             pan.el.addClass('carousel-item-' + dir); // or prev
21960             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21961             cur.el.addClass(lr); // or right
21962             pan.el.addClass(lr);
21963             cur.el.addClass('carousel-item-' +lr); // or right
21964             pan.el.addClass('carousel-item-' +lr);
21965             
21966             
21967             var _this = this;
21968             cur.el.on('transitionend', function() {
21969                 Roo.log("trans end?");
21970                 
21971                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21972                 pan.setActive(true);
21973                 
21974                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21975                 cur.setActive(false);
21976                 
21977                 _this.transition = false;
21978                 
21979             }, this, { single:  true } );
21980             
21981             return true;
21982         }
21983         
21984         cur.setActive(false);
21985         pan.setActive(true);
21986         
21987         return true;
21988         
21989     },
21990     showPanelNext : function()
21991     {
21992         var i = this.indexOfPanel(this.getActivePanel());
21993         
21994         if (i >= this.tabs.length - 1 && !this.autoslide) {
21995             return;
21996         }
21997         
21998         if (i >= this.tabs.length - 1 && this.autoslide) {
21999             i = -1;
22000         }
22001         
22002         this.showPanel(this.tabs[i+1]);
22003     },
22004     
22005     showPanelPrev : function()
22006     {
22007         var i = this.indexOfPanel(this.getActivePanel());
22008         
22009         if (i  < 1 && !this.autoslide) {
22010             return;
22011         }
22012         
22013         if (i < 1 && this.autoslide) {
22014             i = this.tabs.length;
22015         }
22016         
22017         this.showPanel(this.tabs[i-1]);
22018     },
22019     
22020     
22021     addBullet: function()
22022     {
22023         if(!this.bullets || Roo.isTouch){
22024             return;
22025         }
22026         var ctr = this.el.select('.carousel-bullets',true).first();
22027         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22028         var bullet = ctr.createChild({
22029             cls : 'bullet bullet-' + i
22030         },ctr.dom.lastChild);
22031         
22032         
22033         var _this = this;
22034         
22035         bullet.on('click', (function(e, el, o, ii, t){
22036
22037             e.preventDefault();
22038
22039             this.showPanel(ii);
22040
22041             if(this.autoslide && this.slideFn){
22042                 clearInterval(this.slideFn);
22043                 this.slideFn = window.setInterval(function() {
22044                     _this.showPanelNext();
22045                 }, this.timer);
22046             }
22047
22048         }).createDelegate(this, [i, bullet], true));
22049                 
22050         
22051     },
22052      
22053     setActiveBullet : function(i)
22054     {
22055         if(Roo.isTouch){
22056             return;
22057         }
22058         
22059         Roo.each(this.el.select('.bullet', true).elements, function(el){
22060             el.removeClass('selected');
22061         });
22062
22063         var bullet = this.el.select('.bullet-' + i, true).first();
22064         
22065         if(!bullet){
22066             return;
22067         }
22068         
22069         bullet.addClass('selected');
22070     }
22071     
22072     
22073   
22074 });
22075
22076  
22077
22078  
22079  
22080 Roo.apply(Roo.bootstrap.TabGroup, {
22081     
22082     groups: {},
22083      /**
22084     * register a Navigation Group
22085     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22086     */
22087     register : function(navgrp)
22088     {
22089         this.groups[navgrp.navId] = navgrp;
22090         
22091     },
22092     /**
22093     * fetch a Navigation Group based on the navigation ID
22094     * if one does not exist , it will get created.
22095     * @param {string} the navgroup to add
22096     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22097     */
22098     get: function(navId) {
22099         if (typeof(this.groups[navId]) == 'undefined') {
22100             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22101         }
22102         return this.groups[navId] ;
22103     }
22104     
22105     
22106     
22107 });
22108
22109  /*
22110  * - LGPL
22111  *
22112  * TabPanel
22113  * 
22114  */
22115
22116 /**
22117  * @class Roo.bootstrap.TabPanel
22118  * @extends Roo.bootstrap.Component
22119  * Bootstrap TabPanel class
22120  * @cfg {Boolean} active panel active
22121  * @cfg {String} html panel content
22122  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22123  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22124  * @cfg {String} href click to link..
22125  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22126  * 
22127  * 
22128  * @constructor
22129  * Create a new TabPanel
22130  * @param {Object} config The config object
22131  */
22132
22133 Roo.bootstrap.TabPanel = function(config){
22134     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22135     this.addEvents({
22136         /**
22137              * @event changed
22138              * Fires when the active status changes
22139              * @param {Roo.bootstrap.TabPanel} this
22140              * @param {Boolean} state the new state
22141             
22142          */
22143         'changed': true,
22144         /**
22145              * @event beforedeactivate
22146              * Fires before a tab is de-activated - can be used to do validation on a form.
22147              * @param {Roo.bootstrap.TabPanel} this
22148              * @return {Boolean} false if there is an error
22149             
22150          */
22151         'beforedeactivate': true
22152      });
22153     
22154     this.tabId = this.tabId || Roo.id();
22155   
22156 };
22157
22158 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22159     
22160     active: false,
22161     html: false,
22162     tabId: false,
22163     navId : false,
22164     href : '',
22165     touchSlide : false,
22166     getAutoCreate : function(){
22167         
22168         
22169         var cfg = {
22170             tag: 'div',
22171             // item is needed for carousel - not sure if it has any effect otherwise
22172             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22173             html: this.html || ''
22174         };
22175         
22176         if(this.active){
22177             cfg.cls += ' active';
22178         }
22179         
22180         if(this.tabId){
22181             cfg.tabId = this.tabId;
22182         }
22183         
22184         
22185         
22186         return cfg;
22187     },
22188     
22189     initEvents:  function()
22190     {
22191         var p = this.parent();
22192         
22193         this.navId = this.navId || p.navId;
22194         
22195         if (typeof(this.navId) != 'undefined') {
22196             // not really needed.. but just in case.. parent should be a NavGroup.
22197             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22198             
22199             tg.register(this);
22200             
22201             var i = tg.tabs.length - 1;
22202             
22203             if(this.active && tg.bullets > 0 && i < tg.bullets){
22204                 tg.setActiveBullet(i);
22205             }
22206         }
22207         
22208         this.el.on('click', this.onClick, this);
22209         
22210         if(Roo.isTouch && this.touchSlide){
22211             this.el.on("touchstart", this.onTouchStart, this);
22212             this.el.on("touchmove", this.onTouchMove, this);
22213             this.el.on("touchend", this.onTouchEnd, this);
22214         }
22215         
22216     },
22217     
22218     onRender : function(ct, position)
22219     {
22220         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22221     },
22222     
22223     setActive : function(state)
22224     {
22225         Roo.log("panel - set active " + this.tabId + "=" + state);
22226         
22227         this.active = state;
22228         if (!state) {
22229             this.el.removeClass('active');
22230             
22231         } else  if (!this.el.hasClass('active')) {
22232             this.el.addClass('active');
22233         }
22234         
22235         this.fireEvent('changed', this, state);
22236     },
22237     
22238     onClick : function(e)
22239     {
22240         e.preventDefault();
22241         
22242         if(!this.href.length){
22243             return;
22244         }
22245         
22246         window.location.href = this.href;
22247     },
22248     
22249     startX : 0,
22250     startY : 0,
22251     endX : 0,
22252     endY : 0,
22253     swiping : false,
22254     
22255     onTouchStart : function(e)
22256     {
22257         this.swiping = false;
22258         
22259         this.startX = e.browserEvent.touches[0].clientX;
22260         this.startY = e.browserEvent.touches[0].clientY;
22261     },
22262     
22263     onTouchMove : function(e)
22264     {
22265         this.swiping = true;
22266         
22267         this.endX = e.browserEvent.touches[0].clientX;
22268         this.endY = e.browserEvent.touches[0].clientY;
22269     },
22270     
22271     onTouchEnd : function(e)
22272     {
22273         if(!this.swiping){
22274             this.onClick(e);
22275             return;
22276         }
22277         
22278         var tabGroup = this.parent();
22279         
22280         if(this.endX > this.startX){ // swiping right
22281             tabGroup.showPanelPrev();
22282             return;
22283         }
22284         
22285         if(this.startX > this.endX){ // swiping left
22286             tabGroup.showPanelNext();
22287             return;
22288         }
22289     }
22290     
22291     
22292 });
22293  
22294
22295  
22296
22297  /*
22298  * - LGPL
22299  *
22300  * DateField
22301  * 
22302  */
22303
22304 /**
22305  * @class Roo.bootstrap.DateField
22306  * @extends Roo.bootstrap.Input
22307  * Bootstrap DateField class
22308  * @cfg {Number} weekStart default 0
22309  * @cfg {String} viewMode default empty, (months|years)
22310  * @cfg {String} minViewMode default empty, (months|years)
22311  * @cfg {Number} startDate default -Infinity
22312  * @cfg {Number} endDate default Infinity
22313  * @cfg {Boolean} todayHighlight default false
22314  * @cfg {Boolean} todayBtn default false
22315  * @cfg {Boolean} calendarWeeks default false
22316  * @cfg {Object} daysOfWeekDisabled default empty
22317  * @cfg {Boolean} singleMode default false (true | false)
22318  * 
22319  * @cfg {Boolean} keyboardNavigation default true
22320  * @cfg {String} language default en
22321  * 
22322  * @constructor
22323  * Create a new DateField
22324  * @param {Object} config The config object
22325  */
22326
22327 Roo.bootstrap.DateField = function(config){
22328     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22329      this.addEvents({
22330             /**
22331              * @event show
22332              * Fires when this field show.
22333              * @param {Roo.bootstrap.DateField} this
22334              * @param {Mixed} date The date value
22335              */
22336             show : true,
22337             /**
22338              * @event show
22339              * Fires when this field hide.
22340              * @param {Roo.bootstrap.DateField} this
22341              * @param {Mixed} date The date value
22342              */
22343             hide : true,
22344             /**
22345              * @event select
22346              * Fires when select a date.
22347              * @param {Roo.bootstrap.DateField} this
22348              * @param {Mixed} date The date value
22349              */
22350             select : true,
22351             /**
22352              * @event beforeselect
22353              * Fires when before select a date.
22354              * @param {Roo.bootstrap.DateField} this
22355              * @param {Mixed} date The date value
22356              */
22357             beforeselect : true
22358         });
22359 };
22360
22361 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22362     
22363     /**
22364      * @cfg {String} format
22365      * The default date format string which can be overriden for localization support.  The format must be
22366      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22367      */
22368     format : "m/d/y",
22369     /**
22370      * @cfg {String} altFormats
22371      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22372      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22373      */
22374     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22375     
22376     weekStart : 0,
22377     
22378     viewMode : '',
22379     
22380     minViewMode : '',
22381     
22382     todayHighlight : false,
22383     
22384     todayBtn: false,
22385     
22386     language: 'en',
22387     
22388     keyboardNavigation: true,
22389     
22390     calendarWeeks: false,
22391     
22392     startDate: -Infinity,
22393     
22394     endDate: Infinity,
22395     
22396     daysOfWeekDisabled: [],
22397     
22398     _events: [],
22399     
22400     singleMode : false,
22401     
22402     UTCDate: function()
22403     {
22404         return new Date(Date.UTC.apply(Date, arguments));
22405     },
22406     
22407     UTCToday: function()
22408     {
22409         var today = new Date();
22410         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22411     },
22412     
22413     getDate: function() {
22414             var d = this.getUTCDate();
22415             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22416     },
22417     
22418     getUTCDate: function() {
22419             return this.date;
22420     },
22421     
22422     setDate: function(d) {
22423             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22424     },
22425     
22426     setUTCDate: function(d) {
22427             this.date = d;
22428             this.setValue(this.formatDate(this.date));
22429     },
22430         
22431     onRender: function(ct, position)
22432     {
22433         
22434         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22435         
22436         this.language = this.language || 'en';
22437         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22438         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22439         
22440         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22441         this.format = this.format || 'm/d/y';
22442         this.isInline = false;
22443         this.isInput = true;
22444         this.component = this.el.select('.add-on', true).first() || false;
22445         this.component = (this.component && this.component.length === 0) ? false : this.component;
22446         this.hasInput = this.component && this.inputEl().length;
22447         
22448         if (typeof(this.minViewMode === 'string')) {
22449             switch (this.minViewMode) {
22450                 case 'months':
22451                     this.minViewMode = 1;
22452                     break;
22453                 case 'years':
22454                     this.minViewMode = 2;
22455                     break;
22456                 default:
22457                     this.minViewMode = 0;
22458                     break;
22459             }
22460         }
22461         
22462         if (typeof(this.viewMode === 'string')) {
22463             switch (this.viewMode) {
22464                 case 'months':
22465                     this.viewMode = 1;
22466                     break;
22467                 case 'years':
22468                     this.viewMode = 2;
22469                     break;
22470                 default:
22471                     this.viewMode = 0;
22472                     break;
22473             }
22474         }
22475                 
22476         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22477         
22478 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22479         
22480         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22481         
22482         this.picker().on('mousedown', this.onMousedown, this);
22483         this.picker().on('click', this.onClick, this);
22484         
22485         this.picker().addClass('datepicker-dropdown');
22486         
22487         this.startViewMode = this.viewMode;
22488         
22489         if(this.singleMode){
22490             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22491                 v.setVisibilityMode(Roo.Element.DISPLAY);
22492                 v.hide();
22493             });
22494             
22495             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22496                 v.setStyle('width', '189px');
22497             });
22498         }
22499         
22500         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22501             if(!this.calendarWeeks){
22502                 v.remove();
22503                 return;
22504             }
22505             
22506             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22507             v.attr('colspan', function(i, val){
22508                 return parseInt(val) + 1;
22509             });
22510         });
22511                         
22512         
22513         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22514         
22515         this.setStartDate(this.startDate);
22516         this.setEndDate(this.endDate);
22517         
22518         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22519         
22520         this.fillDow();
22521         this.fillMonths();
22522         this.update();
22523         this.showMode();
22524         
22525         if(this.isInline) {
22526             this.showPopup();
22527         }
22528     },
22529     
22530     picker : function()
22531     {
22532         return this.pickerEl;
22533 //        return this.el.select('.datepicker', true).first();
22534     },
22535     
22536     fillDow: function()
22537     {
22538         var dowCnt = this.weekStart;
22539         
22540         var dow = {
22541             tag: 'tr',
22542             cn: [
22543                 
22544             ]
22545         };
22546         
22547         if(this.calendarWeeks){
22548             dow.cn.push({
22549                 tag: 'th',
22550                 cls: 'cw',
22551                 html: '&nbsp;'
22552             })
22553         }
22554         
22555         while (dowCnt < this.weekStart + 7) {
22556             dow.cn.push({
22557                 tag: 'th',
22558                 cls: 'dow',
22559                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22560             });
22561         }
22562         
22563         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22564     },
22565     
22566     fillMonths: function()
22567     {    
22568         var i = 0;
22569         var months = this.picker().select('>.datepicker-months td', true).first();
22570         
22571         months.dom.innerHTML = '';
22572         
22573         while (i < 12) {
22574             var month = {
22575                 tag: 'span',
22576                 cls: 'month',
22577                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22578             };
22579             
22580             months.createChild(month);
22581         }
22582         
22583     },
22584     
22585     update: function()
22586     {
22587         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;
22588         
22589         if (this.date < this.startDate) {
22590             this.viewDate = new Date(this.startDate);
22591         } else if (this.date > this.endDate) {
22592             this.viewDate = new Date(this.endDate);
22593         } else {
22594             this.viewDate = new Date(this.date);
22595         }
22596         
22597         this.fill();
22598     },
22599     
22600     fill: function() 
22601     {
22602         var d = new Date(this.viewDate),
22603                 year = d.getUTCFullYear(),
22604                 month = d.getUTCMonth(),
22605                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22606                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22607                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22608                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22609                 currentDate = this.date && this.date.valueOf(),
22610                 today = this.UTCToday();
22611         
22612         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22613         
22614 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22615         
22616 //        this.picker.select('>tfoot th.today').
22617 //                                              .text(dates[this.language].today)
22618 //                                              .toggle(this.todayBtn !== false);
22619     
22620         this.updateNavArrows();
22621         this.fillMonths();
22622                                                 
22623         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22624         
22625         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22626          
22627         prevMonth.setUTCDate(day);
22628         
22629         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22630         
22631         var nextMonth = new Date(prevMonth);
22632         
22633         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22634         
22635         nextMonth = nextMonth.valueOf();
22636         
22637         var fillMonths = false;
22638         
22639         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22640         
22641         while(prevMonth.valueOf() <= nextMonth) {
22642             var clsName = '';
22643             
22644             if (prevMonth.getUTCDay() === this.weekStart) {
22645                 if(fillMonths){
22646                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22647                 }
22648                     
22649                 fillMonths = {
22650                     tag: 'tr',
22651                     cn: []
22652                 };
22653                 
22654                 if(this.calendarWeeks){
22655                     // ISO 8601: First week contains first thursday.
22656                     // ISO also states week starts on Monday, but we can be more abstract here.
22657                     var
22658                     // Start of current week: based on weekstart/current date
22659                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22660                     // Thursday of this week
22661                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22662                     // First Thursday of year, year from thursday
22663                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22664                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22665                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22666                     
22667                     fillMonths.cn.push({
22668                         tag: 'td',
22669                         cls: 'cw',
22670                         html: calWeek
22671                     });
22672                 }
22673             }
22674             
22675             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22676                 clsName += ' old';
22677             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22678                 clsName += ' new';
22679             }
22680             if (this.todayHighlight &&
22681                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22682                 prevMonth.getUTCMonth() == today.getMonth() &&
22683                 prevMonth.getUTCDate() == today.getDate()) {
22684                 clsName += ' today';
22685             }
22686             
22687             if (currentDate && prevMonth.valueOf() === currentDate) {
22688                 clsName += ' active';
22689             }
22690             
22691             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22692                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22693                     clsName += ' disabled';
22694             }
22695             
22696             fillMonths.cn.push({
22697                 tag: 'td',
22698                 cls: 'day ' + clsName,
22699                 html: prevMonth.getDate()
22700             });
22701             
22702             prevMonth.setDate(prevMonth.getDate()+1);
22703         }
22704           
22705         var currentYear = this.date && this.date.getUTCFullYear();
22706         var currentMonth = this.date && this.date.getUTCMonth();
22707         
22708         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22709         
22710         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22711             v.removeClass('active');
22712             
22713             if(currentYear === year && k === currentMonth){
22714                 v.addClass('active');
22715             }
22716             
22717             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22718                 v.addClass('disabled');
22719             }
22720             
22721         });
22722         
22723         
22724         year = parseInt(year/10, 10) * 10;
22725         
22726         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22727         
22728         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22729         
22730         year -= 1;
22731         for (var i = -1; i < 11; i++) {
22732             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22733                 tag: 'span',
22734                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22735                 html: year
22736             });
22737             
22738             year += 1;
22739         }
22740     },
22741     
22742     showMode: function(dir) 
22743     {
22744         if (dir) {
22745             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22746         }
22747         
22748         Roo.each(this.picker().select('>div',true).elements, function(v){
22749             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22750             v.hide();
22751         });
22752         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22753     },
22754     
22755     place: function()
22756     {
22757         if(this.isInline) {
22758             return;
22759         }
22760         
22761         this.picker().removeClass(['bottom', 'top']);
22762         
22763         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22764             /*
22765              * place to the top of element!
22766              *
22767              */
22768             
22769             this.picker().addClass('top');
22770             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22771             
22772             return;
22773         }
22774         
22775         this.picker().addClass('bottom');
22776         
22777         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22778     },
22779     
22780     parseDate : function(value)
22781     {
22782         if(!value || value instanceof Date){
22783             return value;
22784         }
22785         var v = Date.parseDate(value, this.format);
22786         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22787             v = Date.parseDate(value, 'Y-m-d');
22788         }
22789         if(!v && this.altFormats){
22790             if(!this.altFormatsArray){
22791                 this.altFormatsArray = this.altFormats.split("|");
22792             }
22793             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22794                 v = Date.parseDate(value, this.altFormatsArray[i]);
22795             }
22796         }
22797         return v;
22798     },
22799     
22800     formatDate : function(date, fmt)
22801     {   
22802         return (!date || !(date instanceof Date)) ?
22803         date : date.dateFormat(fmt || this.format);
22804     },
22805     
22806     onFocus : function()
22807     {
22808         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22809         this.showPopup();
22810     },
22811     
22812     onBlur : function()
22813     {
22814         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22815         
22816         var d = this.inputEl().getValue();
22817         
22818         this.setValue(d);
22819                 
22820         this.hidePopup();
22821     },
22822     
22823     showPopup : function()
22824     {
22825         this.picker().show();
22826         this.update();
22827         this.place();
22828         
22829         this.fireEvent('showpopup', this, this.date);
22830     },
22831     
22832     hidePopup : function()
22833     {
22834         if(this.isInline) {
22835             return;
22836         }
22837         this.picker().hide();
22838         this.viewMode = this.startViewMode;
22839         this.showMode();
22840         
22841         this.fireEvent('hidepopup', this, this.date);
22842         
22843     },
22844     
22845     onMousedown: function(e)
22846     {
22847         e.stopPropagation();
22848         e.preventDefault();
22849     },
22850     
22851     keyup: function(e)
22852     {
22853         Roo.bootstrap.DateField.superclass.keyup.call(this);
22854         this.update();
22855     },
22856
22857     setValue: function(v)
22858     {
22859         if(this.fireEvent('beforeselect', this, v) !== false){
22860             var d = new Date(this.parseDate(v) ).clearTime();
22861         
22862             if(isNaN(d.getTime())){
22863                 this.date = this.viewDate = '';
22864                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22865                 return;
22866             }
22867
22868             v = this.formatDate(d);
22869
22870             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22871
22872             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22873
22874             this.update();
22875
22876             this.fireEvent('select', this, this.date);
22877         }
22878     },
22879     
22880     getValue: function()
22881     {
22882         return this.formatDate(this.date);
22883     },
22884     
22885     fireKey: function(e)
22886     {
22887         if (!this.picker().isVisible()){
22888             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22889                 this.showPopup();
22890             }
22891             return;
22892         }
22893         
22894         var dateChanged = false,
22895         dir, day, month,
22896         newDate, newViewDate;
22897         
22898         switch(e.keyCode){
22899             case 27: // escape
22900                 this.hidePopup();
22901                 e.preventDefault();
22902                 break;
22903             case 37: // left
22904             case 39: // right
22905                 if (!this.keyboardNavigation) {
22906                     break;
22907                 }
22908                 dir = e.keyCode == 37 ? -1 : 1;
22909                 
22910                 if (e.ctrlKey){
22911                     newDate = this.moveYear(this.date, dir);
22912                     newViewDate = this.moveYear(this.viewDate, dir);
22913                 } else if (e.shiftKey){
22914                     newDate = this.moveMonth(this.date, dir);
22915                     newViewDate = this.moveMonth(this.viewDate, dir);
22916                 } else {
22917                     newDate = new Date(this.date);
22918                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22919                     newViewDate = new Date(this.viewDate);
22920                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22921                 }
22922                 if (this.dateWithinRange(newDate)){
22923                     this.date = newDate;
22924                     this.viewDate = newViewDate;
22925                     this.setValue(this.formatDate(this.date));
22926 //                    this.update();
22927                     e.preventDefault();
22928                     dateChanged = true;
22929                 }
22930                 break;
22931             case 38: // up
22932             case 40: // down
22933                 if (!this.keyboardNavigation) {
22934                     break;
22935                 }
22936                 dir = e.keyCode == 38 ? -1 : 1;
22937                 if (e.ctrlKey){
22938                     newDate = this.moveYear(this.date, dir);
22939                     newViewDate = this.moveYear(this.viewDate, dir);
22940                 } else if (e.shiftKey){
22941                     newDate = this.moveMonth(this.date, dir);
22942                     newViewDate = this.moveMonth(this.viewDate, dir);
22943                 } else {
22944                     newDate = new Date(this.date);
22945                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22946                     newViewDate = new Date(this.viewDate);
22947                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22948                 }
22949                 if (this.dateWithinRange(newDate)){
22950                     this.date = newDate;
22951                     this.viewDate = newViewDate;
22952                     this.setValue(this.formatDate(this.date));
22953 //                    this.update();
22954                     e.preventDefault();
22955                     dateChanged = true;
22956                 }
22957                 break;
22958             case 13: // enter
22959                 this.setValue(this.formatDate(this.date));
22960                 this.hidePopup();
22961                 e.preventDefault();
22962                 break;
22963             case 9: // tab
22964                 this.setValue(this.formatDate(this.date));
22965                 this.hidePopup();
22966                 break;
22967             case 16: // shift
22968             case 17: // ctrl
22969             case 18: // alt
22970                 break;
22971             default :
22972                 this.hidePopup();
22973                 
22974         }
22975     },
22976     
22977     
22978     onClick: function(e) 
22979     {
22980         e.stopPropagation();
22981         e.preventDefault();
22982         
22983         var target = e.getTarget();
22984         
22985         if(target.nodeName.toLowerCase() === 'i'){
22986             target = Roo.get(target).dom.parentNode;
22987         }
22988         
22989         var nodeName = target.nodeName;
22990         var className = target.className;
22991         var html = target.innerHTML;
22992         //Roo.log(nodeName);
22993         
22994         switch(nodeName.toLowerCase()) {
22995             case 'th':
22996                 switch(className) {
22997                     case 'switch':
22998                         this.showMode(1);
22999                         break;
23000                     case 'prev':
23001                     case 'next':
23002                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23003                         switch(this.viewMode){
23004                                 case 0:
23005                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23006                                         break;
23007                                 case 1:
23008                                 case 2:
23009                                         this.viewDate = this.moveYear(this.viewDate, dir);
23010                                         break;
23011                         }
23012                         this.fill();
23013                         break;
23014                     case 'today':
23015                         var date = new Date();
23016                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23017 //                        this.fill()
23018                         this.setValue(this.formatDate(this.date));
23019                         
23020                         this.hidePopup();
23021                         break;
23022                 }
23023                 break;
23024             case 'span':
23025                 if (className.indexOf('disabled') < 0) {
23026                 if (!this.viewDate) {
23027                     this.viewDate = new Date();
23028                 }
23029                 this.viewDate.setUTCDate(1);
23030                     if (className.indexOf('month') > -1) {
23031                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23032                     } else {
23033                         var year = parseInt(html, 10) || 0;
23034                         this.viewDate.setUTCFullYear(year);
23035                         
23036                     }
23037                     
23038                     if(this.singleMode){
23039                         this.setValue(this.formatDate(this.viewDate));
23040                         this.hidePopup();
23041                         return;
23042                     }
23043                     
23044                     this.showMode(-1);
23045                     this.fill();
23046                 }
23047                 break;
23048                 
23049             case 'td':
23050                 //Roo.log(className);
23051                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23052                     var day = parseInt(html, 10) || 1;
23053                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23054                         month = (this.viewDate || new Date()).getUTCMonth();
23055
23056                     if (className.indexOf('old') > -1) {
23057                         if(month === 0 ){
23058                             month = 11;
23059                             year -= 1;
23060                         }else{
23061                             month -= 1;
23062                         }
23063                     } else if (className.indexOf('new') > -1) {
23064                         if (month == 11) {
23065                             month = 0;
23066                             year += 1;
23067                         } else {
23068                             month += 1;
23069                         }
23070                     }
23071                     //Roo.log([year,month,day]);
23072                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23073                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23074 //                    this.fill();
23075                     //Roo.log(this.formatDate(this.date));
23076                     this.setValue(this.formatDate(this.date));
23077                     this.hidePopup();
23078                 }
23079                 break;
23080         }
23081     },
23082     
23083     setStartDate: function(startDate)
23084     {
23085         this.startDate = startDate || -Infinity;
23086         if (this.startDate !== -Infinity) {
23087             this.startDate = this.parseDate(this.startDate);
23088         }
23089         this.update();
23090         this.updateNavArrows();
23091     },
23092
23093     setEndDate: function(endDate)
23094     {
23095         this.endDate = endDate || Infinity;
23096         if (this.endDate !== Infinity) {
23097             this.endDate = this.parseDate(this.endDate);
23098         }
23099         this.update();
23100         this.updateNavArrows();
23101     },
23102     
23103     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23104     {
23105         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23106         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23107             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23108         }
23109         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23110             return parseInt(d, 10);
23111         });
23112         this.update();
23113         this.updateNavArrows();
23114     },
23115     
23116     updateNavArrows: function() 
23117     {
23118         if(this.singleMode){
23119             return;
23120         }
23121         
23122         var d = new Date(this.viewDate),
23123         year = d.getUTCFullYear(),
23124         month = d.getUTCMonth();
23125         
23126         Roo.each(this.picker().select('.prev', true).elements, function(v){
23127             v.show();
23128             switch (this.viewMode) {
23129                 case 0:
23130
23131                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23132                         v.hide();
23133                     }
23134                     break;
23135                 case 1:
23136                 case 2:
23137                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23138                         v.hide();
23139                     }
23140                     break;
23141             }
23142         });
23143         
23144         Roo.each(this.picker().select('.next', true).elements, function(v){
23145             v.show();
23146             switch (this.viewMode) {
23147                 case 0:
23148
23149                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23150                         v.hide();
23151                     }
23152                     break;
23153                 case 1:
23154                 case 2:
23155                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23156                         v.hide();
23157                     }
23158                     break;
23159             }
23160         })
23161     },
23162     
23163     moveMonth: function(date, dir)
23164     {
23165         if (!dir) {
23166             return date;
23167         }
23168         var new_date = new Date(date.valueOf()),
23169         day = new_date.getUTCDate(),
23170         month = new_date.getUTCMonth(),
23171         mag = Math.abs(dir),
23172         new_month, test;
23173         dir = dir > 0 ? 1 : -1;
23174         if (mag == 1){
23175             test = dir == -1
23176             // If going back one month, make sure month is not current month
23177             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23178             ? function(){
23179                 return new_date.getUTCMonth() == month;
23180             }
23181             // If going forward one month, make sure month is as expected
23182             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23183             : function(){
23184                 return new_date.getUTCMonth() != new_month;
23185             };
23186             new_month = month + dir;
23187             new_date.setUTCMonth(new_month);
23188             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23189             if (new_month < 0 || new_month > 11) {
23190                 new_month = (new_month + 12) % 12;
23191             }
23192         } else {
23193             // For magnitudes >1, move one month at a time...
23194             for (var i=0; i<mag; i++) {
23195                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23196                 new_date = this.moveMonth(new_date, dir);
23197             }
23198             // ...then reset the day, keeping it in the new month
23199             new_month = new_date.getUTCMonth();
23200             new_date.setUTCDate(day);
23201             test = function(){
23202                 return new_month != new_date.getUTCMonth();
23203             };
23204         }
23205         // Common date-resetting loop -- if date is beyond end of month, make it
23206         // end of month
23207         while (test()){
23208             new_date.setUTCDate(--day);
23209             new_date.setUTCMonth(new_month);
23210         }
23211         return new_date;
23212     },
23213
23214     moveYear: function(date, dir)
23215     {
23216         return this.moveMonth(date, dir*12);
23217     },
23218
23219     dateWithinRange: function(date)
23220     {
23221         return date >= this.startDate && date <= this.endDate;
23222     },
23223
23224     
23225     remove: function() 
23226     {
23227         this.picker().remove();
23228     },
23229     
23230     validateValue : function(value)
23231     {
23232         if(this.getVisibilityEl().hasClass('hidden')){
23233             return true;
23234         }
23235         
23236         if(value.length < 1)  {
23237             if(this.allowBlank){
23238                 return true;
23239             }
23240             return false;
23241         }
23242         
23243         if(value.length < this.minLength){
23244             return false;
23245         }
23246         if(value.length > this.maxLength){
23247             return false;
23248         }
23249         if(this.vtype){
23250             var vt = Roo.form.VTypes;
23251             if(!vt[this.vtype](value, this)){
23252                 return false;
23253             }
23254         }
23255         if(typeof this.validator == "function"){
23256             var msg = this.validator(value);
23257             if(msg !== true){
23258                 return false;
23259             }
23260         }
23261         
23262         if(this.regex && !this.regex.test(value)){
23263             return false;
23264         }
23265         
23266         if(typeof(this.parseDate(value)) == 'undefined'){
23267             return false;
23268         }
23269         
23270         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23271             return false;
23272         }      
23273         
23274         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23275             return false;
23276         } 
23277         
23278         
23279         return true;
23280     },
23281     
23282     reset : function()
23283     {
23284         this.date = this.viewDate = '';
23285         
23286         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23287     }
23288    
23289 });
23290
23291 Roo.apply(Roo.bootstrap.DateField,  {
23292     
23293     head : {
23294         tag: 'thead',
23295         cn: [
23296         {
23297             tag: 'tr',
23298             cn: [
23299             {
23300                 tag: 'th',
23301                 cls: 'prev',
23302                 html: '<i class="fa fa-arrow-left"/>'
23303             },
23304             {
23305                 tag: 'th',
23306                 cls: 'switch',
23307                 colspan: '5'
23308             },
23309             {
23310                 tag: 'th',
23311                 cls: 'next',
23312                 html: '<i class="fa fa-arrow-right"/>'
23313             }
23314
23315             ]
23316         }
23317         ]
23318     },
23319     
23320     content : {
23321         tag: 'tbody',
23322         cn: [
23323         {
23324             tag: 'tr',
23325             cn: [
23326             {
23327                 tag: 'td',
23328                 colspan: '7'
23329             }
23330             ]
23331         }
23332         ]
23333     },
23334     
23335     footer : {
23336         tag: 'tfoot',
23337         cn: [
23338         {
23339             tag: 'tr',
23340             cn: [
23341             {
23342                 tag: 'th',
23343                 colspan: '7',
23344                 cls: 'today'
23345             }
23346                     
23347             ]
23348         }
23349         ]
23350     },
23351     
23352     dates:{
23353         en: {
23354             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23355             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23356             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23357             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23358             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23359             today: "Today"
23360         }
23361     },
23362     
23363     modes: [
23364     {
23365         clsName: 'days',
23366         navFnc: 'Month',
23367         navStep: 1
23368     },
23369     {
23370         clsName: 'months',
23371         navFnc: 'FullYear',
23372         navStep: 1
23373     },
23374     {
23375         clsName: 'years',
23376         navFnc: 'FullYear',
23377         navStep: 10
23378     }]
23379 });
23380
23381 Roo.apply(Roo.bootstrap.DateField,  {
23382   
23383     template : {
23384         tag: 'div',
23385         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23386         cn: [
23387         {
23388             tag: 'div',
23389             cls: 'datepicker-days',
23390             cn: [
23391             {
23392                 tag: 'table',
23393                 cls: 'table-condensed',
23394                 cn:[
23395                 Roo.bootstrap.DateField.head,
23396                 {
23397                     tag: 'tbody'
23398                 },
23399                 Roo.bootstrap.DateField.footer
23400                 ]
23401             }
23402             ]
23403         },
23404         {
23405             tag: 'div',
23406             cls: 'datepicker-months',
23407             cn: [
23408             {
23409                 tag: 'table',
23410                 cls: 'table-condensed',
23411                 cn:[
23412                 Roo.bootstrap.DateField.head,
23413                 Roo.bootstrap.DateField.content,
23414                 Roo.bootstrap.DateField.footer
23415                 ]
23416             }
23417             ]
23418         },
23419         {
23420             tag: 'div',
23421             cls: 'datepicker-years',
23422             cn: [
23423             {
23424                 tag: 'table',
23425                 cls: 'table-condensed',
23426                 cn:[
23427                 Roo.bootstrap.DateField.head,
23428                 Roo.bootstrap.DateField.content,
23429                 Roo.bootstrap.DateField.footer
23430                 ]
23431             }
23432             ]
23433         }
23434         ]
23435     }
23436 });
23437
23438  
23439
23440  /*
23441  * - LGPL
23442  *
23443  * TimeField
23444  * 
23445  */
23446
23447 /**
23448  * @class Roo.bootstrap.TimeField
23449  * @extends Roo.bootstrap.Input
23450  * Bootstrap DateField class
23451  * 
23452  * 
23453  * @constructor
23454  * Create a new TimeField
23455  * @param {Object} config The config object
23456  */
23457
23458 Roo.bootstrap.TimeField = function(config){
23459     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23460     this.addEvents({
23461             /**
23462              * @event show
23463              * Fires when this field show.
23464              * @param {Roo.bootstrap.DateField} thisthis
23465              * @param {Mixed} date The date value
23466              */
23467             show : true,
23468             /**
23469              * @event show
23470              * Fires when this field hide.
23471              * @param {Roo.bootstrap.DateField} this
23472              * @param {Mixed} date The date value
23473              */
23474             hide : true,
23475             /**
23476              * @event select
23477              * Fires when select a date.
23478              * @param {Roo.bootstrap.DateField} this
23479              * @param {Mixed} date The date value
23480              */
23481             select : true
23482         });
23483 };
23484
23485 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23486     
23487     /**
23488      * @cfg {String} format
23489      * The default time format string which can be overriden for localization support.  The format must be
23490      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23491      */
23492     format : "H:i",
23493
23494     getAutoCreate : function()
23495     {
23496         this.after = '<i class="fa far fa-clock"></i>';
23497         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23498         
23499          
23500     },
23501     onRender: function(ct, position)
23502     {
23503         
23504         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23505                 
23506         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23507         
23508         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23509         
23510         this.pop = this.picker().select('>.datepicker-time',true).first();
23511         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23512         
23513         this.picker().on('mousedown', this.onMousedown, this);
23514         this.picker().on('click', this.onClick, this);
23515         
23516         this.picker().addClass('datepicker-dropdown');
23517     
23518         this.fillTime();
23519         this.update();
23520             
23521         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23522         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23523         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23524         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23525         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23526         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23527
23528     },
23529     
23530     fireKey: function(e){
23531         if (!this.picker().isVisible()){
23532             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23533                 this.show();
23534             }
23535             return;
23536         }
23537
23538         e.preventDefault();
23539         
23540         switch(e.keyCode){
23541             case 27: // escape
23542                 this.hide();
23543                 break;
23544             case 37: // left
23545             case 39: // right
23546                 this.onTogglePeriod();
23547                 break;
23548             case 38: // up
23549                 this.onIncrementMinutes();
23550                 break;
23551             case 40: // down
23552                 this.onDecrementMinutes();
23553                 break;
23554             case 13: // enter
23555             case 9: // tab
23556                 this.setTime();
23557                 break;
23558         }
23559     },
23560     
23561     onClick: function(e) {
23562         e.stopPropagation();
23563         e.preventDefault();
23564     },
23565     
23566     picker : function()
23567     {
23568         return this.pickerEl;
23569     },
23570     
23571     fillTime: function()
23572     {    
23573         var time = this.pop.select('tbody', true).first();
23574         
23575         time.dom.innerHTML = '';
23576         
23577         time.createChild({
23578             tag: 'tr',
23579             cn: [
23580                 {
23581                     tag: 'td',
23582                     cn: [
23583                         {
23584                             tag: 'a',
23585                             href: '#',
23586                             cls: 'btn',
23587                             cn: [
23588                                 {
23589                                     tag: 'i',
23590                                     cls: 'hours-up fa fas fa-chevron-up'
23591                                 }
23592                             ]
23593                         } 
23594                     ]
23595                 },
23596                 {
23597                     tag: 'td',
23598                     cls: 'separator'
23599                 },
23600                 {
23601                     tag: 'td',
23602                     cn: [
23603                         {
23604                             tag: 'a',
23605                             href: '#',
23606                             cls: 'btn',
23607                             cn: [
23608                                 {
23609                                     tag: 'i',
23610                                     cls: 'minutes-up fa fas fa-chevron-up'
23611                                 }
23612                             ]
23613                         }
23614                     ]
23615                 },
23616                 {
23617                     tag: 'td',
23618                     cls: 'separator'
23619                 }
23620             ]
23621         });
23622         
23623         time.createChild({
23624             tag: 'tr',
23625             cn: [
23626                 {
23627                     tag: 'td',
23628                     cn: [
23629                         {
23630                             tag: 'span',
23631                             cls: 'timepicker-hour',
23632                             html: '00'
23633                         }  
23634                     ]
23635                 },
23636                 {
23637                     tag: 'td',
23638                     cls: 'separator',
23639                     html: ':'
23640                 },
23641                 {
23642                     tag: 'td',
23643                     cn: [
23644                         {
23645                             tag: 'span',
23646                             cls: 'timepicker-minute',
23647                             html: '00'
23648                         }  
23649                     ]
23650                 },
23651                 {
23652                     tag: 'td',
23653                     cls: 'separator'
23654                 },
23655                 {
23656                     tag: 'td',
23657                     cn: [
23658                         {
23659                             tag: 'button',
23660                             type: 'button',
23661                             cls: 'btn btn-primary period',
23662                             html: 'AM'
23663                             
23664                         }
23665                     ]
23666                 }
23667             ]
23668         });
23669         
23670         time.createChild({
23671             tag: 'tr',
23672             cn: [
23673                 {
23674                     tag: 'td',
23675                     cn: [
23676                         {
23677                             tag: 'a',
23678                             href: '#',
23679                             cls: 'btn',
23680                             cn: [
23681                                 {
23682                                     tag: 'span',
23683                                     cls: 'hours-down fa fas fa-chevron-down'
23684                                 }
23685                             ]
23686                         }
23687                     ]
23688                 },
23689                 {
23690                     tag: 'td',
23691                     cls: 'separator'
23692                 },
23693                 {
23694                     tag: 'td',
23695                     cn: [
23696                         {
23697                             tag: 'a',
23698                             href: '#',
23699                             cls: 'btn',
23700                             cn: [
23701                                 {
23702                                     tag: 'span',
23703                                     cls: 'minutes-down fa fas fa-chevron-down'
23704                                 }
23705                             ]
23706                         }
23707                     ]
23708                 },
23709                 {
23710                     tag: 'td',
23711                     cls: 'separator'
23712                 }
23713             ]
23714         });
23715         
23716     },
23717     
23718     update: function()
23719     {
23720         
23721         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23722         
23723         this.fill();
23724     },
23725     
23726     fill: function() 
23727     {
23728         var hours = this.time.getHours();
23729         var minutes = this.time.getMinutes();
23730         var period = 'AM';
23731         
23732         if(hours > 11){
23733             period = 'PM';
23734         }
23735         
23736         if(hours == 0){
23737             hours = 12;
23738         }
23739         
23740         
23741         if(hours > 12){
23742             hours = hours - 12;
23743         }
23744         
23745         if(hours < 10){
23746             hours = '0' + hours;
23747         }
23748         
23749         if(minutes < 10){
23750             minutes = '0' + minutes;
23751         }
23752         
23753         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23754         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23755         this.pop.select('button', true).first().dom.innerHTML = period;
23756         
23757     },
23758     
23759     place: function()
23760     {   
23761         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23762         
23763         var cls = ['bottom'];
23764         
23765         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23766             cls.pop();
23767             cls.push('top');
23768         }
23769         
23770         cls.push('right');
23771         
23772         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23773             cls.pop();
23774             cls.push('left');
23775         }
23776         //this.picker().setXY(20000,20000);
23777         this.picker().addClass(cls.join('-'));
23778         
23779         var _this = this;
23780         
23781         Roo.each(cls, function(c){
23782             if(c == 'bottom'){
23783                 (function() {
23784                  //  
23785                 }).defer(200);
23786                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23787                 //_this.picker().setTop(_this.inputEl().getHeight());
23788                 return;
23789             }
23790             if(c == 'top'){
23791                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23792                 
23793                 //_this.picker().setTop(0 - _this.picker().getHeight());
23794                 return;
23795             }
23796             /*
23797             if(c == 'left'){
23798                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23799                 return;
23800             }
23801             if(c == 'right'){
23802                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23803                 return;
23804             }
23805             */
23806         });
23807         
23808     },
23809   
23810     onFocus : function()
23811     {
23812         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23813         this.show();
23814     },
23815     
23816     onBlur : function()
23817     {
23818         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23819         this.hide();
23820     },
23821     
23822     show : function()
23823     {
23824         this.picker().show();
23825         this.pop.show();
23826         this.update();
23827         this.place();
23828         
23829         this.fireEvent('show', this, this.date);
23830     },
23831     
23832     hide : function()
23833     {
23834         this.picker().hide();
23835         this.pop.hide();
23836         
23837         this.fireEvent('hide', this, this.date);
23838     },
23839     
23840     setTime : function()
23841     {
23842         this.hide();
23843         this.setValue(this.time.format(this.format));
23844         
23845         this.fireEvent('select', this, this.date);
23846         
23847         
23848     },
23849     
23850     onMousedown: function(e){
23851         e.stopPropagation();
23852         e.preventDefault();
23853     },
23854     
23855     onIncrementHours: function()
23856     {
23857         Roo.log('onIncrementHours');
23858         this.time = this.time.add(Date.HOUR, 1);
23859         this.update();
23860         
23861     },
23862     
23863     onDecrementHours: function()
23864     {
23865         Roo.log('onDecrementHours');
23866         this.time = this.time.add(Date.HOUR, -1);
23867         this.update();
23868     },
23869     
23870     onIncrementMinutes: function()
23871     {
23872         Roo.log('onIncrementMinutes');
23873         this.time = this.time.add(Date.MINUTE, 1);
23874         this.update();
23875     },
23876     
23877     onDecrementMinutes: function()
23878     {
23879         Roo.log('onDecrementMinutes');
23880         this.time = this.time.add(Date.MINUTE, -1);
23881         this.update();
23882     },
23883     
23884     onTogglePeriod: function()
23885     {
23886         Roo.log('onTogglePeriod');
23887         this.time = this.time.add(Date.HOUR, 12);
23888         this.update();
23889     }
23890     
23891    
23892 });
23893  
23894
23895 Roo.apply(Roo.bootstrap.TimeField,  {
23896   
23897     template : {
23898         tag: 'div',
23899         cls: 'datepicker dropdown-menu',
23900         cn: [
23901             {
23902                 tag: 'div',
23903                 cls: 'datepicker-time',
23904                 cn: [
23905                 {
23906                     tag: 'table',
23907                     cls: 'table-condensed',
23908                     cn:[
23909                         {
23910                             tag: 'tbody',
23911                             cn: [
23912                                 {
23913                                     tag: 'tr',
23914                                     cn: [
23915                                     {
23916                                         tag: 'td',
23917                                         colspan: '7'
23918                                     }
23919                                     ]
23920                                 }
23921                             ]
23922                         },
23923                         {
23924                             tag: 'tfoot',
23925                             cn: [
23926                                 {
23927                                     tag: 'tr',
23928                                     cn: [
23929                                     {
23930                                         tag: 'th',
23931                                         colspan: '7',
23932                                         cls: '',
23933                                         cn: [
23934                                             {
23935                                                 tag: 'button',
23936                                                 cls: 'btn btn-info ok',
23937                                                 html: 'OK'
23938                                             }
23939                                         ]
23940                                     }
23941                     
23942                                     ]
23943                                 }
23944                             ]
23945                         }
23946                     ]
23947                 }
23948                 ]
23949             }
23950         ]
23951     }
23952 });
23953
23954  
23955
23956  /*
23957  * - LGPL
23958  *
23959  * MonthField
23960  * 
23961  */
23962
23963 /**
23964  * @class Roo.bootstrap.MonthField
23965  * @extends Roo.bootstrap.Input
23966  * Bootstrap MonthField class
23967  * 
23968  * @cfg {String} language default en
23969  * 
23970  * @constructor
23971  * Create a new MonthField
23972  * @param {Object} config The config object
23973  */
23974
23975 Roo.bootstrap.MonthField = function(config){
23976     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23977     
23978     this.addEvents({
23979         /**
23980          * @event show
23981          * Fires when this field show.
23982          * @param {Roo.bootstrap.MonthField} this
23983          * @param {Mixed} date The date value
23984          */
23985         show : true,
23986         /**
23987          * @event show
23988          * Fires when this field hide.
23989          * @param {Roo.bootstrap.MonthField} this
23990          * @param {Mixed} date The date value
23991          */
23992         hide : true,
23993         /**
23994          * @event select
23995          * Fires when select a date.
23996          * @param {Roo.bootstrap.MonthField} this
23997          * @param {String} oldvalue The old value
23998          * @param {String} newvalue The new value
23999          */
24000         select : true
24001     });
24002 };
24003
24004 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
24005     
24006     onRender: function(ct, position)
24007     {
24008         
24009         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24010         
24011         this.language = this.language || 'en';
24012         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24013         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24014         
24015         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24016         this.isInline = false;
24017         this.isInput = true;
24018         this.component = this.el.select('.add-on', true).first() || false;
24019         this.component = (this.component && this.component.length === 0) ? false : this.component;
24020         this.hasInput = this.component && this.inputEL().length;
24021         
24022         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24023         
24024         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24025         
24026         this.picker().on('mousedown', this.onMousedown, this);
24027         this.picker().on('click', this.onClick, this);
24028         
24029         this.picker().addClass('datepicker-dropdown');
24030         
24031         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24032             v.setStyle('width', '189px');
24033         });
24034         
24035         this.fillMonths();
24036         
24037         this.update();
24038         
24039         if(this.isInline) {
24040             this.show();
24041         }
24042         
24043     },
24044     
24045     setValue: function(v, suppressEvent)
24046     {   
24047         var o = this.getValue();
24048         
24049         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24050         
24051         this.update();
24052
24053         if(suppressEvent !== true){
24054             this.fireEvent('select', this, o, v);
24055         }
24056         
24057     },
24058     
24059     getValue: function()
24060     {
24061         return this.value;
24062     },
24063     
24064     onClick: function(e) 
24065     {
24066         e.stopPropagation();
24067         e.preventDefault();
24068         
24069         var target = e.getTarget();
24070         
24071         if(target.nodeName.toLowerCase() === 'i'){
24072             target = Roo.get(target).dom.parentNode;
24073         }
24074         
24075         var nodeName = target.nodeName;
24076         var className = target.className;
24077         var html = target.innerHTML;
24078         
24079         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24080             return;
24081         }
24082         
24083         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24084         
24085         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24086         
24087         this.hide();
24088                         
24089     },
24090     
24091     picker : function()
24092     {
24093         return this.pickerEl;
24094     },
24095     
24096     fillMonths: function()
24097     {    
24098         var i = 0;
24099         var months = this.picker().select('>.datepicker-months td', true).first();
24100         
24101         months.dom.innerHTML = '';
24102         
24103         while (i < 12) {
24104             var month = {
24105                 tag: 'span',
24106                 cls: 'month',
24107                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24108             };
24109             
24110             months.createChild(month);
24111         }
24112         
24113     },
24114     
24115     update: function()
24116     {
24117         var _this = this;
24118         
24119         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24120             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24121         }
24122         
24123         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24124             e.removeClass('active');
24125             
24126             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24127                 e.addClass('active');
24128             }
24129         })
24130     },
24131     
24132     place: function()
24133     {
24134         if(this.isInline) {
24135             return;
24136         }
24137         
24138         this.picker().removeClass(['bottom', 'top']);
24139         
24140         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24141             /*
24142              * place to the top of element!
24143              *
24144              */
24145             
24146             this.picker().addClass('top');
24147             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24148             
24149             return;
24150         }
24151         
24152         this.picker().addClass('bottom');
24153         
24154         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24155     },
24156     
24157     onFocus : function()
24158     {
24159         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24160         this.show();
24161     },
24162     
24163     onBlur : function()
24164     {
24165         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24166         
24167         var d = this.inputEl().getValue();
24168         
24169         this.setValue(d);
24170                 
24171         this.hide();
24172     },
24173     
24174     show : function()
24175     {
24176         this.picker().show();
24177         this.picker().select('>.datepicker-months', true).first().show();
24178         this.update();
24179         this.place();
24180         
24181         this.fireEvent('show', this, this.date);
24182     },
24183     
24184     hide : function()
24185     {
24186         if(this.isInline) {
24187             return;
24188         }
24189         this.picker().hide();
24190         this.fireEvent('hide', this, this.date);
24191         
24192     },
24193     
24194     onMousedown: function(e)
24195     {
24196         e.stopPropagation();
24197         e.preventDefault();
24198     },
24199     
24200     keyup: function(e)
24201     {
24202         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24203         this.update();
24204     },
24205
24206     fireKey: function(e)
24207     {
24208         if (!this.picker().isVisible()){
24209             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24210                 this.show();
24211             }
24212             return;
24213         }
24214         
24215         var dir;
24216         
24217         switch(e.keyCode){
24218             case 27: // escape
24219                 this.hide();
24220                 e.preventDefault();
24221                 break;
24222             case 37: // left
24223             case 39: // right
24224                 dir = e.keyCode == 37 ? -1 : 1;
24225                 
24226                 this.vIndex = this.vIndex + dir;
24227                 
24228                 if(this.vIndex < 0){
24229                     this.vIndex = 0;
24230                 }
24231                 
24232                 if(this.vIndex > 11){
24233                     this.vIndex = 11;
24234                 }
24235                 
24236                 if(isNaN(this.vIndex)){
24237                     this.vIndex = 0;
24238                 }
24239                 
24240                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24241                 
24242                 break;
24243             case 38: // up
24244             case 40: // down
24245                 
24246                 dir = e.keyCode == 38 ? -1 : 1;
24247                 
24248                 this.vIndex = this.vIndex + dir * 4;
24249                 
24250                 if(this.vIndex < 0){
24251                     this.vIndex = 0;
24252                 }
24253                 
24254                 if(this.vIndex > 11){
24255                     this.vIndex = 11;
24256                 }
24257                 
24258                 if(isNaN(this.vIndex)){
24259                     this.vIndex = 0;
24260                 }
24261                 
24262                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24263                 break;
24264                 
24265             case 13: // enter
24266                 
24267                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24268                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24269                 }
24270                 
24271                 this.hide();
24272                 e.preventDefault();
24273                 break;
24274             case 9: // tab
24275                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24276                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24277                 }
24278                 this.hide();
24279                 break;
24280             case 16: // shift
24281             case 17: // ctrl
24282             case 18: // alt
24283                 break;
24284             default :
24285                 this.hide();
24286                 
24287         }
24288     },
24289     
24290     remove: function() 
24291     {
24292         this.picker().remove();
24293     }
24294    
24295 });
24296
24297 Roo.apply(Roo.bootstrap.MonthField,  {
24298     
24299     content : {
24300         tag: 'tbody',
24301         cn: [
24302         {
24303             tag: 'tr',
24304             cn: [
24305             {
24306                 tag: 'td',
24307                 colspan: '7'
24308             }
24309             ]
24310         }
24311         ]
24312     },
24313     
24314     dates:{
24315         en: {
24316             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24317             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24318         }
24319     }
24320 });
24321
24322 Roo.apply(Roo.bootstrap.MonthField,  {
24323   
24324     template : {
24325         tag: 'div',
24326         cls: 'datepicker dropdown-menu roo-dynamic',
24327         cn: [
24328             {
24329                 tag: 'div',
24330                 cls: 'datepicker-months',
24331                 cn: [
24332                 {
24333                     tag: 'table',
24334                     cls: 'table-condensed',
24335                     cn:[
24336                         Roo.bootstrap.DateField.content
24337                     ]
24338                 }
24339                 ]
24340             }
24341         ]
24342     }
24343 });
24344
24345  
24346
24347  
24348  /*
24349  * - LGPL
24350  *
24351  * CheckBox
24352  * 
24353  */
24354
24355 /**
24356  * @class Roo.bootstrap.CheckBox
24357  * @extends Roo.bootstrap.Input
24358  * Bootstrap CheckBox class
24359  * 
24360  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24361  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24362  * @cfg {String} boxLabel The text that appears beside the checkbox
24363  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24364  * @cfg {Boolean} checked initnal the element
24365  * @cfg {Boolean} inline inline the element (default false)
24366  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24367  * @cfg {String} tooltip label tooltip
24368  * 
24369  * @constructor
24370  * Create a new CheckBox
24371  * @param {Object} config The config object
24372  */
24373
24374 Roo.bootstrap.CheckBox = function(config){
24375     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24376    
24377     this.addEvents({
24378         /**
24379         * @event check
24380         * Fires when the element is checked or unchecked.
24381         * @param {Roo.bootstrap.CheckBox} this This input
24382         * @param {Boolean} checked The new checked value
24383         */
24384        check : true,
24385        /**
24386         * @event click
24387         * Fires when the element is click.
24388         * @param {Roo.bootstrap.CheckBox} this This input
24389         */
24390        click : true
24391     });
24392     
24393 };
24394
24395 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24396   
24397     inputType: 'checkbox',
24398     inputValue: 1,
24399     valueOff: 0,
24400     boxLabel: false,
24401     checked: false,
24402     weight : false,
24403     inline: false,
24404     tooltip : '',
24405     
24406     // checkbox success does not make any sense really.. 
24407     invalidClass : "",
24408     validClass : "",
24409     
24410     
24411     getAutoCreate : function()
24412     {
24413         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24414         
24415         var id = Roo.id();
24416         
24417         var cfg = {};
24418         
24419         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24420         
24421         if(this.inline){
24422             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24423         }
24424         
24425         var input =  {
24426             tag: 'input',
24427             id : id,
24428             type : this.inputType,
24429             value : this.inputValue,
24430             cls : 'roo-' + this.inputType, //'form-box',
24431             placeholder : this.placeholder || ''
24432             
24433         };
24434         
24435         if(this.inputType != 'radio'){
24436             var hidden =  {
24437                 tag: 'input',
24438                 type : 'hidden',
24439                 cls : 'roo-hidden-value',
24440                 value : this.checked ? this.inputValue : this.valueOff
24441             };
24442         }
24443         
24444             
24445         if (this.weight) { // Validity check?
24446             cfg.cls += " " + this.inputType + "-" + this.weight;
24447         }
24448         
24449         if (this.disabled) {
24450             input.disabled=true;
24451         }
24452         
24453         if(this.checked){
24454             input.checked = this.checked;
24455         }
24456         
24457         if (this.name) {
24458             
24459             input.name = this.name;
24460             
24461             if(this.inputType != 'radio'){
24462                 hidden.name = this.name;
24463                 input.name = '_hidden_' + this.name;
24464             }
24465         }
24466         
24467         if (this.size) {
24468             input.cls += ' input-' + this.size;
24469         }
24470         
24471         var settings=this;
24472         
24473         ['xs','sm','md','lg'].map(function(size){
24474             if (settings[size]) {
24475                 cfg.cls += ' col-' + size + '-' + settings[size];
24476             }
24477         });
24478         
24479         var inputblock = input;
24480          
24481         if (this.before || this.after) {
24482             
24483             inputblock = {
24484                 cls : 'input-group',
24485                 cn :  [] 
24486             };
24487             
24488             if (this.before) {
24489                 inputblock.cn.push({
24490                     tag :'span',
24491                     cls : 'input-group-addon',
24492                     html : this.before
24493                 });
24494             }
24495             
24496             inputblock.cn.push(input);
24497             
24498             if(this.inputType != 'radio'){
24499                 inputblock.cn.push(hidden);
24500             }
24501             
24502             if (this.after) {
24503                 inputblock.cn.push({
24504                     tag :'span',
24505                     cls : 'input-group-addon',
24506                     html : this.after
24507                 });
24508             }
24509             
24510         }
24511         var boxLabelCfg = false;
24512         
24513         if(this.boxLabel){
24514            
24515             boxLabelCfg = {
24516                 tag: 'label',
24517                 //'for': id, // box label is handled by onclick - so no for...
24518                 cls: 'box-label',
24519                 html: this.boxLabel
24520             };
24521             if(this.tooltip){
24522                 boxLabelCfg.tooltip = this.tooltip;
24523             }
24524              
24525         }
24526         
24527         
24528         if (align ==='left' && this.fieldLabel.length) {
24529 //                Roo.log("left and has label");
24530             cfg.cn = [
24531                 {
24532                     tag: 'label',
24533                     'for' :  id,
24534                     cls : 'control-label',
24535                     html : this.fieldLabel
24536                 },
24537                 {
24538                     cls : "", 
24539                     cn: [
24540                         inputblock
24541                     ]
24542                 }
24543             ];
24544             
24545             if (boxLabelCfg) {
24546                 cfg.cn[1].cn.push(boxLabelCfg);
24547             }
24548             
24549             if(this.labelWidth > 12){
24550                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24551             }
24552             
24553             if(this.labelWidth < 13 && this.labelmd == 0){
24554                 this.labelmd = this.labelWidth;
24555             }
24556             
24557             if(this.labellg > 0){
24558                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24559                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24560             }
24561             
24562             if(this.labelmd > 0){
24563                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24564                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24565             }
24566             
24567             if(this.labelsm > 0){
24568                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24569                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24570             }
24571             
24572             if(this.labelxs > 0){
24573                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24574                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24575             }
24576             
24577         } else if ( this.fieldLabel.length) {
24578 //                Roo.log(" label");
24579                 cfg.cn = [
24580                    
24581                     {
24582                         tag: this.boxLabel ? 'span' : 'label',
24583                         'for': id,
24584                         cls: 'control-label box-input-label',
24585                         //cls : 'input-group-addon',
24586                         html : this.fieldLabel
24587                     },
24588                     
24589                     inputblock
24590                     
24591                 ];
24592                 if (boxLabelCfg) {
24593                     cfg.cn.push(boxLabelCfg);
24594                 }
24595
24596         } else {
24597             
24598 //                Roo.log(" no label && no align");
24599                 cfg.cn = [  inputblock ] ;
24600                 if (boxLabelCfg) {
24601                     cfg.cn.push(boxLabelCfg);
24602                 }
24603
24604                 
24605         }
24606         
24607        
24608         
24609         if(this.inputType != 'radio'){
24610             cfg.cn.push(hidden);
24611         }
24612         
24613         return cfg;
24614         
24615     },
24616     
24617     /**
24618      * return the real input element.
24619      */
24620     inputEl: function ()
24621     {
24622         return this.el.select('input.roo-' + this.inputType,true).first();
24623     },
24624     hiddenEl: function ()
24625     {
24626         return this.el.select('input.roo-hidden-value',true).first();
24627     },
24628     
24629     labelEl: function()
24630     {
24631         return this.el.select('label.control-label',true).first();
24632     },
24633     /* depricated... */
24634     
24635     label: function()
24636     {
24637         return this.labelEl();
24638     },
24639     
24640     boxLabelEl: function()
24641     {
24642         return this.el.select('label.box-label',true).first();
24643     },
24644     
24645     initEvents : function()
24646     {
24647 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24648         
24649         this.inputEl().on('click', this.onClick,  this);
24650         
24651         if (this.boxLabel) { 
24652             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24653         }
24654         
24655         this.startValue = this.getValue();
24656         
24657         if(this.groupId){
24658             Roo.bootstrap.CheckBox.register(this);
24659         }
24660     },
24661     
24662     onClick : function(e)
24663     {   
24664         if(this.fireEvent('click', this, e) !== false){
24665             this.setChecked(!this.checked);
24666         }
24667         
24668     },
24669     
24670     setChecked : function(state,suppressEvent)
24671     {
24672         this.startValue = this.getValue();
24673
24674         if(this.inputType == 'radio'){
24675             
24676             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24677                 e.dom.checked = false;
24678             });
24679             
24680             this.inputEl().dom.checked = true;
24681             
24682             this.inputEl().dom.value = this.inputValue;
24683             
24684             if(suppressEvent !== true){
24685                 this.fireEvent('check', this, true);
24686             }
24687             
24688             this.validate();
24689             
24690             return;
24691         }
24692         
24693         this.checked = state;
24694         
24695         this.inputEl().dom.checked = state;
24696         
24697         
24698         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24699         
24700         if(suppressEvent !== true){
24701             this.fireEvent('check', this, state);
24702         }
24703         
24704         this.validate();
24705     },
24706     
24707     getValue : function()
24708     {
24709         if(this.inputType == 'radio'){
24710             return this.getGroupValue();
24711         }
24712         
24713         return this.hiddenEl().dom.value;
24714         
24715     },
24716     
24717     getGroupValue : function()
24718     {
24719         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24720             return '';
24721         }
24722         
24723         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24724     },
24725     
24726     setValue : function(v,suppressEvent)
24727     {
24728         if(this.inputType == 'radio'){
24729             this.setGroupValue(v, suppressEvent);
24730             return;
24731         }
24732         
24733         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24734         
24735         this.validate();
24736     },
24737     
24738     setGroupValue : function(v, suppressEvent)
24739     {
24740         this.startValue = this.getValue();
24741         
24742         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24743             e.dom.checked = false;
24744             
24745             if(e.dom.value == v){
24746                 e.dom.checked = true;
24747             }
24748         });
24749         
24750         if(suppressEvent !== true){
24751             this.fireEvent('check', this, true);
24752         }
24753
24754         this.validate();
24755         
24756         return;
24757     },
24758     
24759     validate : function()
24760     {
24761         if(this.getVisibilityEl().hasClass('hidden')){
24762             return true;
24763         }
24764         
24765         if(
24766                 this.disabled || 
24767                 (this.inputType == 'radio' && this.validateRadio()) ||
24768                 (this.inputType == 'checkbox' && this.validateCheckbox())
24769         ){
24770             this.markValid();
24771             return true;
24772         }
24773         
24774         this.markInvalid();
24775         return false;
24776     },
24777     
24778     validateRadio : function()
24779     {
24780         if(this.getVisibilityEl().hasClass('hidden')){
24781             return true;
24782         }
24783         
24784         if(this.allowBlank){
24785             return true;
24786         }
24787         
24788         var valid = false;
24789         
24790         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24791             if(!e.dom.checked){
24792                 return;
24793             }
24794             
24795             valid = true;
24796             
24797             return false;
24798         });
24799         
24800         return valid;
24801     },
24802     
24803     validateCheckbox : function()
24804     {
24805         if(!this.groupId){
24806             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24807             //return (this.getValue() == this.inputValue) ? true : false;
24808         }
24809         
24810         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24811         
24812         if(!group){
24813             return false;
24814         }
24815         
24816         var r = false;
24817         
24818         for(var i in group){
24819             if(group[i].el.isVisible(true)){
24820                 r = false;
24821                 break;
24822             }
24823             
24824             r = true;
24825         }
24826         
24827         for(var i in group){
24828             if(r){
24829                 break;
24830             }
24831             
24832             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24833         }
24834         
24835         return r;
24836     },
24837     
24838     /**
24839      * Mark this field as valid
24840      */
24841     markValid : function()
24842     {
24843         var _this = this;
24844         
24845         this.fireEvent('valid', this);
24846         
24847         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24848         
24849         if(this.groupId){
24850             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24851         }
24852         
24853         if(label){
24854             label.markValid();
24855         }
24856
24857         if(this.inputType == 'radio'){
24858             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24859                 var fg = e.findParent('.form-group', false, true);
24860                 if (Roo.bootstrap.version == 3) {
24861                     fg.removeClass([_this.invalidClass, _this.validClass]);
24862                     fg.addClass(_this.validClass);
24863                 } else {
24864                     fg.removeClass(['is-valid', 'is-invalid']);
24865                     fg.addClass('is-valid');
24866                 }
24867             });
24868             
24869             return;
24870         }
24871
24872         if(!this.groupId){
24873             var fg = this.el.findParent('.form-group', false, true);
24874             if (Roo.bootstrap.version == 3) {
24875                 fg.removeClass([this.invalidClass, this.validClass]);
24876                 fg.addClass(this.validClass);
24877             } else {
24878                 fg.removeClass(['is-valid', 'is-invalid']);
24879                 fg.addClass('is-valid');
24880             }
24881             return;
24882         }
24883         
24884         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24885         
24886         if(!group){
24887             return;
24888         }
24889         
24890         for(var i in group){
24891             var fg = group[i].el.findParent('.form-group', false, true);
24892             if (Roo.bootstrap.version == 3) {
24893                 fg.removeClass([this.invalidClass, this.validClass]);
24894                 fg.addClass(this.validClass);
24895             } else {
24896                 fg.removeClass(['is-valid', 'is-invalid']);
24897                 fg.addClass('is-valid');
24898             }
24899         }
24900     },
24901     
24902      /**
24903      * Mark this field as invalid
24904      * @param {String} msg The validation message
24905      */
24906     markInvalid : function(msg)
24907     {
24908         if(this.allowBlank){
24909             return;
24910         }
24911         
24912         var _this = this;
24913         
24914         this.fireEvent('invalid', this, msg);
24915         
24916         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24917         
24918         if(this.groupId){
24919             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24920         }
24921         
24922         if(label){
24923             label.markInvalid();
24924         }
24925             
24926         if(this.inputType == 'radio'){
24927             
24928             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24929                 var fg = e.findParent('.form-group', false, true);
24930                 if (Roo.bootstrap.version == 3) {
24931                     fg.removeClass([_this.invalidClass, _this.validClass]);
24932                     fg.addClass(_this.invalidClass);
24933                 } else {
24934                     fg.removeClass(['is-invalid', 'is-valid']);
24935                     fg.addClass('is-invalid');
24936                 }
24937             });
24938             
24939             return;
24940         }
24941         
24942         if(!this.groupId){
24943             var fg = this.el.findParent('.form-group', false, true);
24944             if (Roo.bootstrap.version == 3) {
24945                 fg.removeClass([_this.invalidClass, _this.validClass]);
24946                 fg.addClass(_this.invalidClass);
24947             } else {
24948                 fg.removeClass(['is-invalid', 'is-valid']);
24949                 fg.addClass('is-invalid');
24950             }
24951             return;
24952         }
24953         
24954         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24955         
24956         if(!group){
24957             return;
24958         }
24959         
24960         for(var i in group){
24961             var fg = group[i].el.findParent('.form-group', false, true);
24962             if (Roo.bootstrap.version == 3) {
24963                 fg.removeClass([_this.invalidClass, _this.validClass]);
24964                 fg.addClass(_this.invalidClass);
24965             } else {
24966                 fg.removeClass(['is-invalid', 'is-valid']);
24967                 fg.addClass('is-invalid');
24968             }
24969         }
24970         
24971     },
24972     
24973     clearInvalid : function()
24974     {
24975         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24976         
24977         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24978         
24979         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24980         
24981         if (label && label.iconEl) {
24982             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24983             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24984         }
24985     },
24986     
24987     disable : function()
24988     {
24989         if(this.inputType != 'radio'){
24990             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24991             return;
24992         }
24993         
24994         var _this = this;
24995         
24996         if(this.rendered){
24997             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24998                 _this.getActionEl().addClass(this.disabledClass);
24999                 e.dom.disabled = true;
25000             });
25001         }
25002         
25003         this.disabled = true;
25004         this.fireEvent("disable", this);
25005         return this;
25006     },
25007
25008     enable : function()
25009     {
25010         if(this.inputType != 'radio'){
25011             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25012             return;
25013         }
25014         
25015         var _this = this;
25016         
25017         if(this.rendered){
25018             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25019                 _this.getActionEl().removeClass(this.disabledClass);
25020                 e.dom.disabled = false;
25021             });
25022         }
25023         
25024         this.disabled = false;
25025         this.fireEvent("enable", this);
25026         return this;
25027     },
25028     
25029     setBoxLabel : function(v)
25030     {
25031         this.boxLabel = v;
25032         
25033         if(this.rendered){
25034             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25035         }
25036     }
25037
25038 });
25039
25040 Roo.apply(Roo.bootstrap.CheckBox, {
25041     
25042     groups: {},
25043     
25044      /**
25045     * register a CheckBox Group
25046     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25047     */
25048     register : function(checkbox)
25049     {
25050         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25051             this.groups[checkbox.groupId] = {};
25052         }
25053         
25054         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25055             return;
25056         }
25057         
25058         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25059         
25060     },
25061     /**
25062     * fetch a CheckBox Group based on the group ID
25063     * @param {string} the group ID
25064     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25065     */
25066     get: function(groupId) {
25067         if (typeof(this.groups[groupId]) == 'undefined') {
25068             return false;
25069         }
25070         
25071         return this.groups[groupId] ;
25072     }
25073     
25074     
25075 });
25076 /*
25077  * - LGPL
25078  *
25079  * RadioItem
25080  * 
25081  */
25082
25083 /**
25084  * @class Roo.bootstrap.Radio
25085  * @extends Roo.bootstrap.Component
25086  * Bootstrap Radio class
25087  * @cfg {String} boxLabel - the label associated
25088  * @cfg {String} value - the value of radio
25089  * 
25090  * @constructor
25091  * Create a new Radio
25092  * @param {Object} config The config object
25093  */
25094 Roo.bootstrap.Radio = function(config){
25095     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25096     
25097 };
25098
25099 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25100     
25101     boxLabel : '',
25102     
25103     value : '',
25104     
25105     getAutoCreate : function()
25106     {
25107         var cfg = {
25108             tag : 'div',
25109             cls : 'form-group radio',
25110             cn : [
25111                 {
25112                     tag : 'label',
25113                     cls : 'box-label',
25114                     html : this.boxLabel
25115                 }
25116             ]
25117         };
25118         
25119         return cfg;
25120     },
25121     
25122     initEvents : function() 
25123     {
25124         this.parent().register(this);
25125         
25126         this.el.on('click', this.onClick, this);
25127         
25128     },
25129     
25130     onClick : function(e)
25131     {
25132         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25133             this.setChecked(true);
25134         }
25135     },
25136     
25137     setChecked : function(state, suppressEvent)
25138     {
25139         this.parent().setValue(this.value, suppressEvent);
25140         
25141     },
25142     
25143     setBoxLabel : function(v)
25144     {
25145         this.boxLabel = v;
25146         
25147         if(this.rendered){
25148             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25149         }
25150     }
25151     
25152 });
25153  
25154
25155  /*
25156  * - LGPL
25157  *
25158  * Input
25159  * 
25160  */
25161
25162 /**
25163  * @class Roo.bootstrap.SecurePass
25164  * @extends Roo.bootstrap.Input
25165  * Bootstrap SecurePass class
25166  *
25167  * 
25168  * @constructor
25169  * Create a new SecurePass
25170  * @param {Object} config The config object
25171  */
25172  
25173 Roo.bootstrap.SecurePass = function (config) {
25174     // these go here, so the translation tool can replace them..
25175     this.errors = {
25176         PwdEmpty: "Please type a password, and then retype it to confirm.",
25177         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25178         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25179         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25180         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25181         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25182         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25183         TooWeak: "Your password is Too Weak."
25184     },
25185     this.meterLabel = "Password strength:";
25186     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25187     this.meterClass = [
25188         "roo-password-meter-tooweak", 
25189         "roo-password-meter-weak", 
25190         "roo-password-meter-medium", 
25191         "roo-password-meter-strong", 
25192         "roo-password-meter-grey"
25193     ];
25194     
25195     this.errors = {};
25196     
25197     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25198 }
25199
25200 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25201     /**
25202      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25203      * {
25204      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25205      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25206      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25207      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25208      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25209      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25210      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25211      * })
25212      */
25213     // private
25214     
25215     meterWidth: 300,
25216     errorMsg :'',    
25217     errors: false,
25218     imageRoot: '/',
25219     /**
25220      * @cfg {String/Object} Label for the strength meter (defaults to
25221      * 'Password strength:')
25222      */
25223     // private
25224     meterLabel: '',
25225     /**
25226      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25227      * ['Weak', 'Medium', 'Strong'])
25228      */
25229     // private    
25230     pwdStrengths: false,    
25231     // private
25232     strength: 0,
25233     // private
25234     _lastPwd: null,
25235     // private
25236     kCapitalLetter: 0,
25237     kSmallLetter: 1,
25238     kDigit: 2,
25239     kPunctuation: 3,
25240     
25241     insecure: false,
25242     // private
25243     initEvents: function ()
25244     {
25245         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25246
25247         if (this.el.is('input[type=password]') && Roo.isSafari) {
25248             this.el.on('keydown', this.SafariOnKeyDown, this);
25249         }
25250
25251         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25252     },
25253     // private
25254     onRender: function (ct, position)
25255     {
25256         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25257         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25258         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25259
25260         this.trigger.createChild({
25261                    cn: [
25262                     {
25263                     //id: 'PwdMeter',
25264                     tag: 'div',
25265                     cls: 'roo-password-meter-grey col-xs-12',
25266                     style: {
25267                         //width: 0,
25268                         //width: this.meterWidth + 'px'                                                
25269                         }
25270                     },
25271                     {                            
25272                          cls: 'roo-password-meter-text'                          
25273                     }
25274                 ]            
25275         });
25276
25277          
25278         if (this.hideTrigger) {
25279             this.trigger.setDisplayed(false);
25280         }
25281         this.setSize(this.width || '', this.height || '');
25282     },
25283     // private
25284     onDestroy: function ()
25285     {
25286         if (this.trigger) {
25287             this.trigger.removeAllListeners();
25288             this.trigger.remove();
25289         }
25290         if (this.wrap) {
25291             this.wrap.remove();
25292         }
25293         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25294     },
25295     // private
25296     checkStrength: function ()
25297     {
25298         var pwd = this.inputEl().getValue();
25299         if (pwd == this._lastPwd) {
25300             return;
25301         }
25302
25303         var strength;
25304         if (this.ClientSideStrongPassword(pwd)) {
25305             strength = 3;
25306         } else if (this.ClientSideMediumPassword(pwd)) {
25307             strength = 2;
25308         } else if (this.ClientSideWeakPassword(pwd)) {
25309             strength = 1;
25310         } else {
25311             strength = 0;
25312         }
25313         
25314         Roo.log('strength1: ' + strength);
25315         
25316         //var pm = this.trigger.child('div/div/div').dom;
25317         var pm = this.trigger.child('div/div');
25318         pm.removeClass(this.meterClass);
25319         pm.addClass(this.meterClass[strength]);
25320                 
25321         
25322         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25323                 
25324         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25325         
25326         this._lastPwd = pwd;
25327     },
25328     reset: function ()
25329     {
25330         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25331         
25332         this._lastPwd = '';
25333         
25334         var pm = this.trigger.child('div/div');
25335         pm.removeClass(this.meterClass);
25336         pm.addClass('roo-password-meter-grey');        
25337         
25338         
25339         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25340         
25341         pt.innerHTML = '';
25342         this.inputEl().dom.type='password';
25343     },
25344     // private
25345     validateValue: function (value)
25346     {
25347         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25348             return false;
25349         }
25350         if (value.length == 0) {
25351             if (this.allowBlank) {
25352                 this.clearInvalid();
25353                 return true;
25354             }
25355
25356             this.markInvalid(this.errors.PwdEmpty);
25357             this.errorMsg = this.errors.PwdEmpty;
25358             return false;
25359         }
25360         
25361         if(this.insecure){
25362             return true;
25363         }
25364         
25365         if (!value.match(/[\x21-\x7e]+/)) {
25366             this.markInvalid(this.errors.PwdBadChar);
25367             this.errorMsg = this.errors.PwdBadChar;
25368             return false;
25369         }
25370         if (value.length < 6) {
25371             this.markInvalid(this.errors.PwdShort);
25372             this.errorMsg = this.errors.PwdShort;
25373             return false;
25374         }
25375         if (value.length > 16) {
25376             this.markInvalid(this.errors.PwdLong);
25377             this.errorMsg = this.errors.PwdLong;
25378             return false;
25379         }
25380         var strength;
25381         if (this.ClientSideStrongPassword(value)) {
25382             strength = 3;
25383         } else if (this.ClientSideMediumPassword(value)) {
25384             strength = 2;
25385         } else if (this.ClientSideWeakPassword(value)) {
25386             strength = 1;
25387         } else {
25388             strength = 0;
25389         }
25390
25391         
25392         if (strength < 2) {
25393             //this.markInvalid(this.errors.TooWeak);
25394             this.errorMsg = this.errors.TooWeak;
25395             //return false;
25396         }
25397         
25398         
25399         console.log('strength2: ' + strength);
25400         
25401         //var pm = this.trigger.child('div/div/div').dom;
25402         
25403         var pm = this.trigger.child('div/div');
25404         pm.removeClass(this.meterClass);
25405         pm.addClass(this.meterClass[strength]);
25406                 
25407         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25408                 
25409         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25410         
25411         this.errorMsg = ''; 
25412         return true;
25413     },
25414     // private
25415     CharacterSetChecks: function (type)
25416     {
25417         this.type = type;
25418         this.fResult = false;
25419     },
25420     // private
25421     isctype: function (character, type)
25422     {
25423         switch (type) {  
25424             case this.kCapitalLetter:
25425                 if (character >= 'A' && character <= 'Z') {
25426                     return true;
25427                 }
25428                 break;
25429             
25430             case this.kSmallLetter:
25431                 if (character >= 'a' && character <= 'z') {
25432                     return true;
25433                 }
25434                 break;
25435             
25436             case this.kDigit:
25437                 if (character >= '0' && character <= '9') {
25438                     return true;
25439                 }
25440                 break;
25441             
25442             case this.kPunctuation:
25443                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25444                     return true;
25445                 }
25446                 break;
25447             
25448             default:
25449                 return false;
25450         }
25451
25452     },
25453     // private
25454     IsLongEnough: function (pwd, size)
25455     {
25456         return !(pwd == null || isNaN(size) || pwd.length < size);
25457     },
25458     // private
25459     SpansEnoughCharacterSets: function (word, nb)
25460     {
25461         if (!this.IsLongEnough(word, nb))
25462         {
25463             return false;
25464         }
25465
25466         var characterSetChecks = new Array(
25467             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25468             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25469         );
25470         
25471         for (var index = 0; index < word.length; ++index) {
25472             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25473                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25474                     characterSetChecks[nCharSet].fResult = true;
25475                     break;
25476                 }
25477             }
25478         }
25479
25480         var nCharSets = 0;
25481         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25482             if (characterSetChecks[nCharSet].fResult) {
25483                 ++nCharSets;
25484             }
25485         }
25486
25487         if (nCharSets < nb) {
25488             return false;
25489         }
25490         return true;
25491     },
25492     // private
25493     ClientSideStrongPassword: function (pwd)
25494     {
25495         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25496     },
25497     // private
25498     ClientSideMediumPassword: function (pwd)
25499     {
25500         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25501     },
25502     // private
25503     ClientSideWeakPassword: function (pwd)
25504     {
25505         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25506     }
25507           
25508 })//<script type="text/javascript">
25509
25510 /*
25511  * Based  Ext JS Library 1.1.1
25512  * Copyright(c) 2006-2007, Ext JS, LLC.
25513  * LGPL
25514  *
25515  */
25516  
25517 /**
25518  * @class Roo.HtmlEditorCore
25519  * @extends Roo.Component
25520  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25521  *
25522  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25523  */
25524
25525 Roo.HtmlEditorCore = function(config){
25526     
25527     
25528     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25529     
25530     
25531     this.addEvents({
25532         /**
25533          * @event initialize
25534          * Fires when the editor is fully initialized (including the iframe)
25535          * @param {Roo.HtmlEditorCore} this
25536          */
25537         initialize: true,
25538         /**
25539          * @event activate
25540          * Fires when the editor is first receives the focus. Any insertion must wait
25541          * until after this event.
25542          * @param {Roo.HtmlEditorCore} this
25543          */
25544         activate: true,
25545          /**
25546          * @event beforesync
25547          * Fires before the textarea is updated with content from the editor iframe. Return false
25548          * to cancel the sync.
25549          * @param {Roo.HtmlEditorCore} this
25550          * @param {String} html
25551          */
25552         beforesync: true,
25553          /**
25554          * @event beforepush
25555          * Fires before the iframe editor is updated with content from the textarea. Return false
25556          * to cancel the push.
25557          * @param {Roo.HtmlEditorCore} this
25558          * @param {String} html
25559          */
25560         beforepush: true,
25561          /**
25562          * @event sync
25563          * Fires when the textarea is updated with content from the editor iframe.
25564          * @param {Roo.HtmlEditorCore} this
25565          * @param {String} html
25566          */
25567         sync: true,
25568          /**
25569          * @event push
25570          * Fires when the iframe editor is updated with content from the textarea.
25571          * @param {Roo.HtmlEditorCore} this
25572          * @param {String} html
25573          */
25574         push: true,
25575         
25576         /**
25577          * @event editorevent
25578          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25579          * @param {Roo.HtmlEditorCore} this
25580          */
25581         editorevent: true
25582         
25583     });
25584     
25585     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25586     
25587     // defaults : white / black...
25588     this.applyBlacklists();
25589     
25590     
25591     
25592 };
25593
25594
25595 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25596
25597
25598      /**
25599      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25600      */
25601     
25602     owner : false,
25603     
25604      /**
25605      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25606      *                        Roo.resizable.
25607      */
25608     resizable : false,
25609      /**
25610      * @cfg {Number} height (in pixels)
25611      */   
25612     height: 300,
25613    /**
25614      * @cfg {Number} width (in pixels)
25615      */   
25616     width: 500,
25617     
25618     /**
25619      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25620      * 
25621      */
25622     stylesheets: false,
25623     
25624     /**
25625      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25626      */
25627     allowComments: false,
25628     // id of frame..
25629     frameId: false,
25630     
25631     // private properties
25632     validationEvent : false,
25633     deferHeight: true,
25634     initialized : false,
25635     activated : false,
25636     sourceEditMode : false,
25637     onFocus : Roo.emptyFn,
25638     iframePad:3,
25639     hideMode:'offsets',
25640     
25641     clearUp: true,
25642     
25643     // blacklist + whitelisted elements..
25644     black: false,
25645     white: false,
25646      
25647     bodyCls : '',
25648
25649     /**
25650      * Protected method that will not generally be called directly. It
25651      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25652      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25653      */
25654     getDocMarkup : function(){
25655         // body styles..
25656         var st = '';
25657         
25658         // inherit styels from page...?? 
25659         if (this.stylesheets === false) {
25660             
25661             Roo.get(document.head).select('style').each(function(node) {
25662                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25663             });
25664             
25665             Roo.get(document.head).select('link').each(function(node) { 
25666                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25667             });
25668             
25669         } else if (!this.stylesheets.length) {
25670                 // simple..
25671                 st = '<style type="text/css">' +
25672                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25673                    '</style>';
25674         } else {
25675             for (var i in this.stylesheets) { 
25676                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25677             }
25678             
25679         }
25680         
25681         st +=  '<style type="text/css">' +
25682             'IMG { cursor: pointer } ' +
25683         '</style>';
25684
25685         var cls = 'roo-htmleditor-body';
25686         
25687         if(this.bodyCls.length){
25688             cls += ' ' + this.bodyCls;
25689         }
25690         
25691         return '<html><head>' + st  +
25692             //<style type="text/css">' +
25693             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25694             //'</style>' +
25695             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25696     },
25697
25698     // private
25699     onRender : function(ct, position)
25700     {
25701         var _t = this;
25702         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25703         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25704         
25705         
25706         this.el.dom.style.border = '0 none';
25707         this.el.dom.setAttribute('tabIndex', -1);
25708         this.el.addClass('x-hidden hide');
25709         
25710         
25711         
25712         if(Roo.isIE){ // fix IE 1px bogus margin
25713             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25714         }
25715        
25716         
25717         this.frameId = Roo.id();
25718         
25719          
25720         
25721         var iframe = this.owner.wrap.createChild({
25722             tag: 'iframe',
25723             cls: 'form-control', // bootstrap..
25724             id: this.frameId,
25725             name: this.frameId,
25726             frameBorder : 'no',
25727             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25728         }, this.el
25729         );
25730         
25731         
25732         this.iframe = iframe.dom;
25733
25734          this.assignDocWin();
25735         
25736         this.doc.designMode = 'on';
25737        
25738         this.doc.open();
25739         this.doc.write(this.getDocMarkup());
25740         this.doc.close();
25741
25742         
25743         var task = { // must defer to wait for browser to be ready
25744             run : function(){
25745                 //console.log("run task?" + this.doc.readyState);
25746                 this.assignDocWin();
25747                 if(this.doc.body || this.doc.readyState == 'complete'){
25748                     try {
25749                         this.doc.designMode="on";
25750                     } catch (e) {
25751                         return;
25752                     }
25753                     Roo.TaskMgr.stop(task);
25754                     this.initEditor.defer(10, this);
25755                 }
25756             },
25757             interval : 10,
25758             duration: 10000,
25759             scope: this
25760         };
25761         Roo.TaskMgr.start(task);
25762
25763     },
25764
25765     // private
25766     onResize : function(w, h)
25767     {
25768          Roo.log('resize: ' +w + ',' + h );
25769         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25770         if(!this.iframe){
25771             return;
25772         }
25773         if(typeof w == 'number'){
25774             
25775             this.iframe.style.width = w + 'px';
25776         }
25777         if(typeof h == 'number'){
25778             
25779             this.iframe.style.height = h + 'px';
25780             if(this.doc){
25781                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25782             }
25783         }
25784         
25785     },
25786
25787     /**
25788      * Toggles the editor between standard and source edit mode.
25789      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25790      */
25791     toggleSourceEdit : function(sourceEditMode){
25792         
25793         this.sourceEditMode = sourceEditMode === true;
25794         
25795         if(this.sourceEditMode){
25796  
25797             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25798             
25799         }else{
25800             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25801             //this.iframe.className = '';
25802             this.deferFocus();
25803         }
25804         //this.setSize(this.owner.wrap.getSize());
25805         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25806     },
25807
25808     
25809   
25810
25811     /**
25812      * Protected method that will not generally be called directly. If you need/want
25813      * custom HTML cleanup, this is the method you should override.
25814      * @param {String} html The HTML to be cleaned
25815      * return {String} The cleaned HTML
25816      */
25817     cleanHtml : function(html){
25818         html = String(html);
25819         if(html.length > 5){
25820             if(Roo.isSafari){ // strip safari nonsense
25821                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25822             }
25823         }
25824         if(html == '&nbsp;'){
25825             html = '';
25826         }
25827         return html;
25828     },
25829
25830     /**
25831      * HTML Editor -> Textarea
25832      * Protected method that will not generally be called directly. Syncs the contents
25833      * of the editor iframe with the textarea.
25834      */
25835     syncValue : function(){
25836         if(this.initialized){
25837             var bd = (this.doc.body || this.doc.documentElement);
25838             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25839             var html = bd.innerHTML;
25840             if(Roo.isSafari){
25841                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25842                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25843                 if(m && m[1]){
25844                     html = '<div style="'+m[0]+'">' + html + '</div>';
25845                 }
25846             }
25847             html = this.cleanHtml(html);
25848             // fix up the special chars.. normaly like back quotes in word...
25849             // however we do not want to do this with chinese..
25850             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25851                 
25852                 var cc = match.charCodeAt();
25853
25854                 // Get the character value, handling surrogate pairs
25855                 if (match.length == 2) {
25856                     // It's a surrogate pair, calculate the Unicode code point
25857                     var high = match.charCodeAt(0) - 0xD800;
25858                     var low  = match.charCodeAt(1) - 0xDC00;
25859                     cc = (high * 0x400) + low + 0x10000;
25860                 }  else if (
25861                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25862                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25863                     (cc >= 0xf900 && cc < 0xfb00 )
25864                 ) {
25865                         return match;
25866                 }  
25867          
25868                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25869                 return "&#" + cc + ";";
25870                 
25871                 
25872             });
25873             
25874             
25875              
25876             if(this.owner.fireEvent('beforesync', this, html) !== false){
25877                 this.el.dom.value = html;
25878                 this.owner.fireEvent('sync', this, html);
25879             }
25880         }
25881     },
25882
25883     /**
25884      * Protected method that will not generally be called directly. Pushes the value of the textarea
25885      * into the iframe editor.
25886      */
25887     pushValue : function(){
25888         if(this.initialized){
25889             var v = this.el.dom.value.trim();
25890             
25891 //            if(v.length < 1){
25892 //                v = '&#160;';
25893 //            }
25894             
25895             if(this.owner.fireEvent('beforepush', this, v) !== false){
25896                 var d = (this.doc.body || this.doc.documentElement);
25897                 d.innerHTML = v;
25898                 this.cleanUpPaste();
25899                 this.el.dom.value = d.innerHTML;
25900                 this.owner.fireEvent('push', this, v);
25901             }
25902         }
25903     },
25904
25905     // private
25906     deferFocus : function(){
25907         this.focus.defer(10, this);
25908     },
25909
25910     // doc'ed in Field
25911     focus : function(){
25912         if(this.win && !this.sourceEditMode){
25913             this.win.focus();
25914         }else{
25915             this.el.focus();
25916         }
25917     },
25918     
25919     assignDocWin: function()
25920     {
25921         var iframe = this.iframe;
25922         
25923          if(Roo.isIE){
25924             this.doc = iframe.contentWindow.document;
25925             this.win = iframe.contentWindow;
25926         } else {
25927 //            if (!Roo.get(this.frameId)) {
25928 //                return;
25929 //            }
25930 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25931 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25932             
25933             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25934                 return;
25935             }
25936             
25937             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25938             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25939         }
25940     },
25941     
25942     // private
25943     initEditor : function(){
25944         //console.log("INIT EDITOR");
25945         this.assignDocWin();
25946         
25947         
25948         
25949         this.doc.designMode="on";
25950         this.doc.open();
25951         this.doc.write(this.getDocMarkup());
25952         this.doc.close();
25953         
25954         var dbody = (this.doc.body || this.doc.documentElement);
25955         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25956         // this copies styles from the containing element into thsi one..
25957         // not sure why we need all of this..
25958         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25959         
25960         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25961         //ss['background-attachment'] = 'fixed'; // w3c
25962         dbody.bgProperties = 'fixed'; // ie
25963         //Roo.DomHelper.applyStyles(dbody, ss);
25964         Roo.EventManager.on(this.doc, {
25965             //'mousedown': this.onEditorEvent,
25966             'mouseup': this.onEditorEvent,
25967             'dblclick': this.onEditorEvent,
25968             'click': this.onEditorEvent,
25969             'keyup': this.onEditorEvent,
25970             buffer:100,
25971             scope: this
25972         });
25973         if(Roo.isGecko){
25974             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25975         }
25976         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25977             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25978         }
25979         this.initialized = true;
25980
25981         this.owner.fireEvent('initialize', this);
25982         this.pushValue();
25983     },
25984
25985     // private
25986     onDestroy : function(){
25987         
25988         
25989         
25990         if(this.rendered){
25991             
25992             //for (var i =0; i < this.toolbars.length;i++) {
25993             //    // fixme - ask toolbars for heights?
25994             //    this.toolbars[i].onDestroy();
25995            // }
25996             
25997             //this.wrap.dom.innerHTML = '';
25998             //this.wrap.remove();
25999         }
26000     },
26001
26002     // private
26003     onFirstFocus : function(){
26004         
26005         this.assignDocWin();
26006         
26007         
26008         this.activated = true;
26009          
26010     
26011         if(Roo.isGecko){ // prevent silly gecko errors
26012             this.win.focus();
26013             var s = this.win.getSelection();
26014             if(!s.focusNode || s.focusNode.nodeType != 3){
26015                 var r = s.getRangeAt(0);
26016                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26017                 r.collapse(true);
26018                 this.deferFocus();
26019             }
26020             try{
26021                 this.execCmd('useCSS', true);
26022                 this.execCmd('styleWithCSS', false);
26023             }catch(e){}
26024         }
26025         this.owner.fireEvent('activate', this);
26026     },
26027
26028     // private
26029     adjustFont: function(btn){
26030         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26031         //if(Roo.isSafari){ // safari
26032         //    adjust *= 2;
26033        // }
26034         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26035         if(Roo.isSafari){ // safari
26036             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26037             v =  (v < 10) ? 10 : v;
26038             v =  (v > 48) ? 48 : v;
26039             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26040             
26041         }
26042         
26043         
26044         v = Math.max(1, v+adjust);
26045         
26046         this.execCmd('FontSize', v  );
26047     },
26048
26049     onEditorEvent : function(e)
26050     {
26051         this.owner.fireEvent('editorevent', this, e);
26052       //  this.updateToolbar();
26053         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26054     },
26055
26056     insertTag : function(tg)
26057     {
26058         // could be a bit smarter... -> wrap the current selected tRoo..
26059         if (tg.toLowerCase() == 'span' ||
26060             tg.toLowerCase() == 'code' ||
26061             tg.toLowerCase() == 'sup' ||
26062             tg.toLowerCase() == 'sub' 
26063             ) {
26064             
26065             range = this.createRange(this.getSelection());
26066             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26067             wrappingNode.appendChild(range.extractContents());
26068             range.insertNode(wrappingNode);
26069
26070             return;
26071             
26072             
26073             
26074         }
26075         this.execCmd("formatblock",   tg);
26076         
26077     },
26078     
26079     insertText : function(txt)
26080     {
26081         
26082         
26083         var range = this.createRange();
26084         range.deleteContents();
26085                //alert(Sender.getAttribute('label'));
26086                
26087         range.insertNode(this.doc.createTextNode(txt));
26088     } ,
26089     
26090      
26091
26092     /**
26093      * Executes a Midas editor command on the editor document and performs necessary focus and
26094      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26095      * @param {String} cmd The Midas command
26096      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26097      */
26098     relayCmd : function(cmd, value){
26099         this.win.focus();
26100         this.execCmd(cmd, value);
26101         this.owner.fireEvent('editorevent', this);
26102         //this.updateToolbar();
26103         this.owner.deferFocus();
26104     },
26105
26106     /**
26107      * Executes a Midas editor command directly on the editor document.
26108      * For visual commands, you should use {@link #relayCmd} instead.
26109      * <b>This should only be called after the editor is initialized.</b>
26110      * @param {String} cmd The Midas command
26111      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26112      */
26113     execCmd : function(cmd, value){
26114         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26115         this.syncValue();
26116     },
26117  
26118  
26119    
26120     /**
26121      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26122      * to insert tRoo.
26123      * @param {String} text | dom node.. 
26124      */
26125     insertAtCursor : function(text)
26126     {
26127         
26128         if(!this.activated){
26129             return;
26130         }
26131         /*
26132         if(Roo.isIE){
26133             this.win.focus();
26134             var r = this.doc.selection.createRange();
26135             if(r){
26136                 r.collapse(true);
26137                 r.pasteHTML(text);
26138                 this.syncValue();
26139                 this.deferFocus();
26140             
26141             }
26142             return;
26143         }
26144         */
26145         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26146             this.win.focus();
26147             
26148             
26149             // from jquery ui (MIT licenced)
26150             var range, node;
26151             var win = this.win;
26152             
26153             if (win.getSelection && win.getSelection().getRangeAt) {
26154                 range = win.getSelection().getRangeAt(0);
26155                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26156                 range.insertNode(node);
26157             } else if (win.document.selection && win.document.selection.createRange) {
26158                 // no firefox support
26159                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26160                 win.document.selection.createRange().pasteHTML(txt);
26161             } else {
26162                 // no firefox support
26163                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26164                 this.execCmd('InsertHTML', txt);
26165             } 
26166             
26167             this.syncValue();
26168             
26169             this.deferFocus();
26170         }
26171     },
26172  // private
26173     mozKeyPress : function(e){
26174         if(e.ctrlKey){
26175             var c = e.getCharCode(), cmd;
26176           
26177             if(c > 0){
26178                 c = String.fromCharCode(c).toLowerCase();
26179                 switch(c){
26180                     case 'b':
26181                         cmd = 'bold';
26182                         break;
26183                     case 'i':
26184                         cmd = 'italic';
26185                         break;
26186                     
26187                     case 'u':
26188                         cmd = 'underline';
26189                         break;
26190                     
26191                     case 'v':
26192                         this.cleanUpPaste.defer(100, this);
26193                         return;
26194                         
26195                 }
26196                 if(cmd){
26197                     this.win.focus();
26198                     this.execCmd(cmd);
26199                     this.deferFocus();
26200                     e.preventDefault();
26201                 }
26202                 
26203             }
26204         }
26205     },
26206
26207     // private
26208     fixKeys : function(){ // load time branching for fastest keydown performance
26209         if(Roo.isIE){
26210             return function(e){
26211                 var k = e.getKey(), r;
26212                 if(k == e.TAB){
26213                     e.stopEvent();
26214                     r = this.doc.selection.createRange();
26215                     if(r){
26216                         r.collapse(true);
26217                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26218                         this.deferFocus();
26219                     }
26220                     return;
26221                 }
26222                 
26223                 if(k == e.ENTER){
26224                     r = this.doc.selection.createRange();
26225                     if(r){
26226                         var target = r.parentElement();
26227                         if(!target || target.tagName.toLowerCase() != 'li'){
26228                             e.stopEvent();
26229                             r.pasteHTML('<br />');
26230                             r.collapse(false);
26231                             r.select();
26232                         }
26233                     }
26234                 }
26235                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26236                     this.cleanUpPaste.defer(100, this);
26237                     return;
26238                 }
26239                 
26240                 
26241             };
26242         }else if(Roo.isOpera){
26243             return function(e){
26244                 var k = e.getKey();
26245                 if(k == e.TAB){
26246                     e.stopEvent();
26247                     this.win.focus();
26248                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26249                     this.deferFocus();
26250                 }
26251                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26252                     this.cleanUpPaste.defer(100, this);
26253                     return;
26254                 }
26255                 
26256             };
26257         }else if(Roo.isSafari){
26258             return function(e){
26259                 var k = e.getKey();
26260                 
26261                 if(k == e.TAB){
26262                     e.stopEvent();
26263                     this.execCmd('InsertText','\t');
26264                     this.deferFocus();
26265                     return;
26266                 }
26267                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26268                     this.cleanUpPaste.defer(100, this);
26269                     return;
26270                 }
26271                 
26272              };
26273         }
26274     }(),
26275     
26276     getAllAncestors: function()
26277     {
26278         var p = this.getSelectedNode();
26279         var a = [];
26280         if (!p) {
26281             a.push(p); // push blank onto stack..
26282             p = this.getParentElement();
26283         }
26284         
26285         
26286         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26287             a.push(p);
26288             p = p.parentNode;
26289         }
26290         a.push(this.doc.body);
26291         return a;
26292     },
26293     lastSel : false,
26294     lastSelNode : false,
26295     
26296     
26297     getSelection : function() 
26298     {
26299         this.assignDocWin();
26300         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26301     },
26302     
26303     getSelectedNode: function() 
26304     {
26305         // this may only work on Gecko!!!
26306         
26307         // should we cache this!!!!
26308         
26309         
26310         
26311          
26312         var range = this.createRange(this.getSelection()).cloneRange();
26313         
26314         if (Roo.isIE) {
26315             var parent = range.parentElement();
26316             while (true) {
26317                 var testRange = range.duplicate();
26318                 testRange.moveToElementText(parent);
26319                 if (testRange.inRange(range)) {
26320                     break;
26321                 }
26322                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26323                     break;
26324                 }
26325                 parent = parent.parentElement;
26326             }
26327             return parent;
26328         }
26329         
26330         // is ancestor a text element.
26331         var ac =  range.commonAncestorContainer;
26332         if (ac.nodeType == 3) {
26333             ac = ac.parentNode;
26334         }
26335         
26336         var ar = ac.childNodes;
26337          
26338         var nodes = [];
26339         var other_nodes = [];
26340         var has_other_nodes = false;
26341         for (var i=0;i<ar.length;i++) {
26342             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26343                 continue;
26344             }
26345             // fullly contained node.
26346             
26347             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26348                 nodes.push(ar[i]);
26349                 continue;
26350             }
26351             
26352             // probably selected..
26353             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26354                 other_nodes.push(ar[i]);
26355                 continue;
26356             }
26357             // outer..
26358             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26359                 continue;
26360             }
26361             
26362             
26363             has_other_nodes = true;
26364         }
26365         if (!nodes.length && other_nodes.length) {
26366             nodes= other_nodes;
26367         }
26368         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26369             return false;
26370         }
26371         
26372         return nodes[0];
26373     },
26374     createRange: function(sel)
26375     {
26376         // this has strange effects when using with 
26377         // top toolbar - not sure if it's a great idea.
26378         //this.editor.contentWindow.focus();
26379         if (typeof sel != "undefined") {
26380             try {
26381                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26382             } catch(e) {
26383                 return this.doc.createRange();
26384             }
26385         } else {
26386             return this.doc.createRange();
26387         }
26388     },
26389     getParentElement: function()
26390     {
26391         
26392         this.assignDocWin();
26393         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26394         
26395         var range = this.createRange(sel);
26396          
26397         try {
26398             var p = range.commonAncestorContainer;
26399             while (p.nodeType == 3) { // text node
26400                 p = p.parentNode;
26401             }
26402             return p;
26403         } catch (e) {
26404             return null;
26405         }
26406     
26407     },
26408     /***
26409      *
26410      * Range intersection.. the hard stuff...
26411      *  '-1' = before
26412      *  '0' = hits..
26413      *  '1' = after.
26414      *         [ -- selected range --- ]
26415      *   [fail]                        [fail]
26416      *
26417      *    basically..
26418      *      if end is before start or  hits it. fail.
26419      *      if start is after end or hits it fail.
26420      *
26421      *   if either hits (but other is outside. - then it's not 
26422      *   
26423      *    
26424      **/
26425     
26426     
26427     // @see http://www.thismuchiknow.co.uk/?p=64.
26428     rangeIntersectsNode : function(range, node)
26429     {
26430         var nodeRange = node.ownerDocument.createRange();
26431         try {
26432             nodeRange.selectNode(node);
26433         } catch (e) {
26434             nodeRange.selectNodeContents(node);
26435         }
26436     
26437         var rangeStartRange = range.cloneRange();
26438         rangeStartRange.collapse(true);
26439     
26440         var rangeEndRange = range.cloneRange();
26441         rangeEndRange.collapse(false);
26442     
26443         var nodeStartRange = nodeRange.cloneRange();
26444         nodeStartRange.collapse(true);
26445     
26446         var nodeEndRange = nodeRange.cloneRange();
26447         nodeEndRange.collapse(false);
26448     
26449         return rangeStartRange.compareBoundaryPoints(
26450                  Range.START_TO_START, nodeEndRange) == -1 &&
26451                rangeEndRange.compareBoundaryPoints(
26452                  Range.START_TO_START, nodeStartRange) == 1;
26453         
26454          
26455     },
26456     rangeCompareNode : function(range, node)
26457     {
26458         var nodeRange = node.ownerDocument.createRange();
26459         try {
26460             nodeRange.selectNode(node);
26461         } catch (e) {
26462             nodeRange.selectNodeContents(node);
26463         }
26464         
26465         
26466         range.collapse(true);
26467     
26468         nodeRange.collapse(true);
26469      
26470         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26471         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26472          
26473         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26474         
26475         var nodeIsBefore   =  ss == 1;
26476         var nodeIsAfter    = ee == -1;
26477         
26478         if (nodeIsBefore && nodeIsAfter) {
26479             return 0; // outer
26480         }
26481         if (!nodeIsBefore && nodeIsAfter) {
26482             return 1; //right trailed.
26483         }
26484         
26485         if (nodeIsBefore && !nodeIsAfter) {
26486             return 2;  // left trailed.
26487         }
26488         // fully contined.
26489         return 3;
26490     },
26491
26492     // private? - in a new class?
26493     cleanUpPaste :  function()
26494     {
26495         // cleans up the whole document..
26496         Roo.log('cleanuppaste');
26497         
26498         this.cleanUpChildren(this.doc.body);
26499         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26500         if (clean != this.doc.body.innerHTML) {
26501             this.doc.body.innerHTML = clean;
26502         }
26503         
26504     },
26505     
26506     cleanWordChars : function(input) {// change the chars to hex code
26507         var he = Roo.HtmlEditorCore;
26508         
26509         var output = input;
26510         Roo.each(he.swapCodes, function(sw) { 
26511             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26512             
26513             output = output.replace(swapper, sw[1]);
26514         });
26515         
26516         return output;
26517     },
26518     
26519     
26520     cleanUpChildren : function (n)
26521     {
26522         if (!n.childNodes.length) {
26523             return;
26524         }
26525         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26526            this.cleanUpChild(n.childNodes[i]);
26527         }
26528     },
26529     
26530     
26531         
26532     
26533     cleanUpChild : function (node)
26534     {
26535         var ed = this;
26536         //console.log(node);
26537         if (node.nodeName == "#text") {
26538             // clean up silly Windows -- stuff?
26539             return; 
26540         }
26541         if (node.nodeName == "#comment") {
26542             if (!this.allowComments) {
26543                 node.parentNode.removeChild(node);
26544             }
26545             // clean up silly Windows -- stuff?
26546             return; 
26547         }
26548         var lcname = node.tagName.toLowerCase();
26549         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26550         // whitelist of tags..
26551         
26552         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26553             // remove node.
26554             node.parentNode.removeChild(node);
26555             return;
26556             
26557         }
26558         
26559         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26560         
26561         // spans with no attributes - just remove them..
26562         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26563             remove_keep_children = true;
26564         }
26565         
26566         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26567         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26568         
26569         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26570         //    remove_keep_children = true;
26571         //}
26572         
26573         if (remove_keep_children) {
26574             this.cleanUpChildren(node);
26575             // inserts everything just before this node...
26576             while (node.childNodes.length) {
26577                 var cn = node.childNodes[0];
26578                 node.removeChild(cn);
26579                 node.parentNode.insertBefore(cn, node);
26580             }
26581             node.parentNode.removeChild(node);
26582             return;
26583         }
26584         
26585         if (!node.attributes || !node.attributes.length) {
26586             
26587           
26588             
26589             
26590             this.cleanUpChildren(node);
26591             return;
26592         }
26593         
26594         function cleanAttr(n,v)
26595         {
26596             
26597             if (v.match(/^\./) || v.match(/^\//)) {
26598                 return;
26599             }
26600             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26601                 return;
26602             }
26603             if (v.match(/^#/)) {
26604                 return;
26605             }
26606             if (v.match(/^\{/)) { // allow template editing.
26607                 return;
26608             }
26609 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26610             node.removeAttribute(n);
26611             
26612         }
26613         
26614         var cwhite = this.cwhite;
26615         var cblack = this.cblack;
26616             
26617         function cleanStyle(n,v)
26618         {
26619             if (v.match(/expression/)) { //XSS?? should we even bother..
26620                 node.removeAttribute(n);
26621                 return;
26622             }
26623             
26624             var parts = v.split(/;/);
26625             var clean = [];
26626             
26627             Roo.each(parts, function(p) {
26628                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26629                 if (!p.length) {
26630                     return true;
26631                 }
26632                 var l = p.split(':').shift().replace(/\s+/g,'');
26633                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26634                 
26635                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26636 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26637                     //node.removeAttribute(n);
26638                     return true;
26639                 }
26640                 //Roo.log()
26641                 // only allow 'c whitelisted system attributes'
26642                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26643 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26644                     //node.removeAttribute(n);
26645                     return true;
26646                 }
26647                 
26648                 
26649                  
26650                 
26651                 clean.push(p);
26652                 return true;
26653             });
26654             if (clean.length) { 
26655                 node.setAttribute(n, clean.join(';'));
26656             } else {
26657                 node.removeAttribute(n);
26658             }
26659             
26660         }
26661         
26662         
26663         for (var i = node.attributes.length-1; i > -1 ; i--) {
26664             var a = node.attributes[i];
26665             //console.log(a);
26666             
26667             if (a.name.toLowerCase().substr(0,2)=='on')  {
26668                 node.removeAttribute(a.name);
26669                 continue;
26670             }
26671             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26672                 node.removeAttribute(a.name);
26673                 continue;
26674             }
26675             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26676                 cleanAttr(a.name,a.value); // fixme..
26677                 continue;
26678             }
26679             if (a.name == 'style') {
26680                 cleanStyle(a.name,a.value);
26681                 continue;
26682             }
26683             /// clean up MS crap..
26684             // tecnically this should be a list of valid class'es..
26685             
26686             
26687             if (a.name == 'class') {
26688                 if (a.value.match(/^Mso/)) {
26689                     node.removeAttribute('class');
26690                 }
26691                 
26692                 if (a.value.match(/^body$/)) {
26693                     node.removeAttribute('class');
26694                 }
26695                 continue;
26696             }
26697             
26698             // style cleanup!?
26699             // class cleanup?
26700             
26701         }
26702         
26703         
26704         this.cleanUpChildren(node);
26705         
26706         
26707     },
26708     
26709     /**
26710      * Clean up MS wordisms...
26711      */
26712     cleanWord : function(node)
26713     {
26714         if (!node) {
26715             this.cleanWord(this.doc.body);
26716             return;
26717         }
26718         
26719         if(
26720                 node.nodeName == 'SPAN' &&
26721                 !node.hasAttributes() &&
26722                 node.childNodes.length == 1 &&
26723                 node.firstChild.nodeName == "#text"  
26724         ) {
26725             var textNode = node.firstChild;
26726             node.removeChild(textNode);
26727             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26728                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26729             }
26730             node.parentNode.insertBefore(textNode, node);
26731             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26732                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26733             }
26734             node.parentNode.removeChild(node);
26735         }
26736         
26737         if (node.nodeName == "#text") {
26738             // clean up silly Windows -- stuff?
26739             return; 
26740         }
26741         if (node.nodeName == "#comment") {
26742             node.parentNode.removeChild(node);
26743             // clean up silly Windows -- stuff?
26744             return; 
26745         }
26746         
26747         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26748             node.parentNode.removeChild(node);
26749             return;
26750         }
26751         //Roo.log(node.tagName);
26752         // remove - but keep children..
26753         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26754             //Roo.log('-- removed');
26755             while (node.childNodes.length) {
26756                 var cn = node.childNodes[0];
26757                 node.removeChild(cn);
26758                 node.parentNode.insertBefore(cn, node);
26759                 // move node to parent - and clean it..
26760                 this.cleanWord(cn);
26761             }
26762             node.parentNode.removeChild(node);
26763             /// no need to iterate chidlren = it's got none..
26764             //this.iterateChildren(node, this.cleanWord);
26765             return;
26766         }
26767         // clean styles
26768         if (node.className.length) {
26769             
26770             var cn = node.className.split(/\W+/);
26771             var cna = [];
26772             Roo.each(cn, function(cls) {
26773                 if (cls.match(/Mso[a-zA-Z]+/)) {
26774                     return;
26775                 }
26776                 cna.push(cls);
26777             });
26778             node.className = cna.length ? cna.join(' ') : '';
26779             if (!cna.length) {
26780                 node.removeAttribute("class");
26781             }
26782         }
26783         
26784         if (node.hasAttribute("lang")) {
26785             node.removeAttribute("lang");
26786         }
26787         
26788         if (node.hasAttribute("style")) {
26789             
26790             var styles = node.getAttribute("style").split(";");
26791             var nstyle = [];
26792             Roo.each(styles, function(s) {
26793                 if (!s.match(/:/)) {
26794                     return;
26795                 }
26796                 var kv = s.split(":");
26797                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26798                     return;
26799                 }
26800                 // what ever is left... we allow.
26801                 nstyle.push(s);
26802             });
26803             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26804             if (!nstyle.length) {
26805                 node.removeAttribute('style');
26806             }
26807         }
26808         this.iterateChildren(node, this.cleanWord);
26809         
26810         
26811         
26812     },
26813     /**
26814      * iterateChildren of a Node, calling fn each time, using this as the scole..
26815      * @param {DomNode} node node to iterate children of.
26816      * @param {Function} fn method of this class to call on each item.
26817      */
26818     iterateChildren : function(node, fn)
26819     {
26820         if (!node.childNodes.length) {
26821                 return;
26822         }
26823         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26824            fn.call(this, node.childNodes[i])
26825         }
26826     },
26827     
26828     
26829     /**
26830      * cleanTableWidths.
26831      *
26832      * Quite often pasting from word etc.. results in tables with column and widths.
26833      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26834      *
26835      */
26836     cleanTableWidths : function(node)
26837     {
26838          
26839          
26840         if (!node) {
26841             this.cleanTableWidths(this.doc.body);
26842             return;
26843         }
26844         
26845         // ignore list...
26846         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26847             return; 
26848         }
26849         Roo.log(node.tagName);
26850         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26851             this.iterateChildren(node, this.cleanTableWidths);
26852             return;
26853         }
26854         if (node.hasAttribute('width')) {
26855             node.removeAttribute('width');
26856         }
26857         
26858          
26859         if (node.hasAttribute("style")) {
26860             // pretty basic...
26861             
26862             var styles = node.getAttribute("style").split(";");
26863             var nstyle = [];
26864             Roo.each(styles, function(s) {
26865                 if (!s.match(/:/)) {
26866                     return;
26867                 }
26868                 var kv = s.split(":");
26869                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26870                     return;
26871                 }
26872                 // what ever is left... we allow.
26873                 nstyle.push(s);
26874             });
26875             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26876             if (!nstyle.length) {
26877                 node.removeAttribute('style');
26878             }
26879         }
26880         
26881         this.iterateChildren(node, this.cleanTableWidths);
26882         
26883         
26884     },
26885     
26886     
26887     
26888     
26889     domToHTML : function(currentElement, depth, nopadtext) {
26890         
26891         depth = depth || 0;
26892         nopadtext = nopadtext || false;
26893     
26894         if (!currentElement) {
26895             return this.domToHTML(this.doc.body);
26896         }
26897         
26898         //Roo.log(currentElement);
26899         var j;
26900         var allText = false;
26901         var nodeName = currentElement.nodeName;
26902         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26903         
26904         if  (nodeName == '#text') {
26905             
26906             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26907         }
26908         
26909         
26910         var ret = '';
26911         if (nodeName != 'BODY') {
26912              
26913             var i = 0;
26914             // Prints the node tagName, such as <A>, <IMG>, etc
26915             if (tagName) {
26916                 var attr = [];
26917                 for(i = 0; i < currentElement.attributes.length;i++) {
26918                     // quoting?
26919                     var aname = currentElement.attributes.item(i).name;
26920                     if (!currentElement.attributes.item(i).value.length) {
26921                         continue;
26922                     }
26923                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26924                 }
26925                 
26926                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26927             } 
26928             else {
26929                 
26930                 // eack
26931             }
26932         } else {
26933             tagName = false;
26934         }
26935         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26936             return ret;
26937         }
26938         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26939             nopadtext = true;
26940         }
26941         
26942         
26943         // Traverse the tree
26944         i = 0;
26945         var currentElementChild = currentElement.childNodes.item(i);
26946         var allText = true;
26947         var innerHTML  = '';
26948         lastnode = '';
26949         while (currentElementChild) {
26950             // Formatting code (indent the tree so it looks nice on the screen)
26951             var nopad = nopadtext;
26952             if (lastnode == 'SPAN') {
26953                 nopad  = true;
26954             }
26955             // text
26956             if  (currentElementChild.nodeName == '#text') {
26957                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26958                 toadd = nopadtext ? toadd : toadd.trim();
26959                 if (!nopad && toadd.length > 80) {
26960                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26961                 }
26962                 innerHTML  += toadd;
26963                 
26964                 i++;
26965                 currentElementChild = currentElement.childNodes.item(i);
26966                 lastNode = '';
26967                 continue;
26968             }
26969             allText = false;
26970             
26971             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26972                 
26973             // Recursively traverse the tree structure of the child node
26974             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26975             lastnode = currentElementChild.nodeName;
26976             i++;
26977             currentElementChild=currentElement.childNodes.item(i);
26978         }
26979         
26980         ret += innerHTML;
26981         
26982         if (!allText) {
26983                 // The remaining code is mostly for formatting the tree
26984             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26985         }
26986         
26987         
26988         if (tagName) {
26989             ret+= "</"+tagName+">";
26990         }
26991         return ret;
26992         
26993     },
26994         
26995     applyBlacklists : function()
26996     {
26997         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26998         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26999         
27000         this.white = [];
27001         this.black = [];
27002         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27003             if (b.indexOf(tag) > -1) {
27004                 return;
27005             }
27006             this.white.push(tag);
27007             
27008         }, this);
27009         
27010         Roo.each(w, function(tag) {
27011             if (b.indexOf(tag) > -1) {
27012                 return;
27013             }
27014             if (this.white.indexOf(tag) > -1) {
27015                 return;
27016             }
27017             this.white.push(tag);
27018             
27019         }, this);
27020         
27021         
27022         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27023             if (w.indexOf(tag) > -1) {
27024                 return;
27025             }
27026             this.black.push(tag);
27027             
27028         }, this);
27029         
27030         Roo.each(b, function(tag) {
27031             if (w.indexOf(tag) > -1) {
27032                 return;
27033             }
27034             if (this.black.indexOf(tag) > -1) {
27035                 return;
27036             }
27037             this.black.push(tag);
27038             
27039         }, this);
27040         
27041         
27042         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27043         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27044         
27045         this.cwhite = [];
27046         this.cblack = [];
27047         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27048             if (b.indexOf(tag) > -1) {
27049                 return;
27050             }
27051             this.cwhite.push(tag);
27052             
27053         }, this);
27054         
27055         Roo.each(w, function(tag) {
27056             if (b.indexOf(tag) > -1) {
27057                 return;
27058             }
27059             if (this.cwhite.indexOf(tag) > -1) {
27060                 return;
27061             }
27062             this.cwhite.push(tag);
27063             
27064         }, this);
27065         
27066         
27067         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27068             if (w.indexOf(tag) > -1) {
27069                 return;
27070             }
27071             this.cblack.push(tag);
27072             
27073         }, this);
27074         
27075         Roo.each(b, function(tag) {
27076             if (w.indexOf(tag) > -1) {
27077                 return;
27078             }
27079             if (this.cblack.indexOf(tag) > -1) {
27080                 return;
27081             }
27082             this.cblack.push(tag);
27083             
27084         }, this);
27085     },
27086     
27087     setStylesheets : function(stylesheets)
27088     {
27089         if(typeof(stylesheets) == 'string'){
27090             Roo.get(this.iframe.contentDocument.head).createChild({
27091                 tag : 'link',
27092                 rel : 'stylesheet',
27093                 type : 'text/css',
27094                 href : stylesheets
27095             });
27096             
27097             return;
27098         }
27099         var _this = this;
27100      
27101         Roo.each(stylesheets, function(s) {
27102             if(!s.length){
27103                 return;
27104             }
27105             
27106             Roo.get(_this.iframe.contentDocument.head).createChild({
27107                 tag : 'link',
27108                 rel : 'stylesheet',
27109                 type : 'text/css',
27110                 href : s
27111             });
27112         });
27113
27114         
27115     },
27116     
27117     removeStylesheets : function()
27118     {
27119         var _this = this;
27120         
27121         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27122             s.remove();
27123         });
27124     },
27125     
27126     setStyle : function(style)
27127     {
27128         Roo.get(this.iframe.contentDocument.head).createChild({
27129             tag : 'style',
27130             type : 'text/css',
27131             html : style
27132         });
27133
27134         return;
27135     }
27136     
27137     // hide stuff that is not compatible
27138     /**
27139      * @event blur
27140      * @hide
27141      */
27142     /**
27143      * @event change
27144      * @hide
27145      */
27146     /**
27147      * @event focus
27148      * @hide
27149      */
27150     /**
27151      * @event specialkey
27152      * @hide
27153      */
27154     /**
27155      * @cfg {String} fieldClass @hide
27156      */
27157     /**
27158      * @cfg {String} focusClass @hide
27159      */
27160     /**
27161      * @cfg {String} autoCreate @hide
27162      */
27163     /**
27164      * @cfg {String} inputType @hide
27165      */
27166     /**
27167      * @cfg {String} invalidClass @hide
27168      */
27169     /**
27170      * @cfg {String} invalidText @hide
27171      */
27172     /**
27173      * @cfg {String} msgFx @hide
27174      */
27175     /**
27176      * @cfg {String} validateOnBlur @hide
27177      */
27178 });
27179
27180 Roo.HtmlEditorCore.white = [
27181         'area', 'br', 'img', 'input', 'hr', 'wbr',
27182         
27183        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27184        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27185        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27186        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27187        'table',   'ul',         'xmp', 
27188        
27189        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27190       'thead',   'tr', 
27191      
27192       'dir', 'menu', 'ol', 'ul', 'dl',
27193        
27194       'embed',  'object'
27195 ];
27196
27197
27198 Roo.HtmlEditorCore.black = [
27199     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27200         'applet', // 
27201         'base',   'basefont', 'bgsound', 'blink',  'body', 
27202         'frame',  'frameset', 'head',    'html',   'ilayer', 
27203         'iframe', 'layer',  'link',     'meta',    'object',   
27204         'script', 'style' ,'title',  'xml' // clean later..
27205 ];
27206 Roo.HtmlEditorCore.clean = [
27207     'script', 'style', 'title', 'xml'
27208 ];
27209 Roo.HtmlEditorCore.remove = [
27210     'font'
27211 ];
27212 // attributes..
27213
27214 Roo.HtmlEditorCore.ablack = [
27215     'on'
27216 ];
27217     
27218 Roo.HtmlEditorCore.aclean = [ 
27219     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27220 ];
27221
27222 // protocols..
27223 Roo.HtmlEditorCore.pwhite= [
27224         'http',  'https',  'mailto'
27225 ];
27226
27227 // white listed style attributes.
27228 Roo.HtmlEditorCore.cwhite= [
27229       //  'text-align', /// default is to allow most things..
27230       
27231          
27232 //        'font-size'//??
27233 ];
27234
27235 // black listed style attributes.
27236 Roo.HtmlEditorCore.cblack= [
27237       //  'font-size' -- this can be set by the project 
27238 ];
27239
27240
27241 Roo.HtmlEditorCore.swapCodes   =[ 
27242     [    8211, "&#8211;" ], 
27243     [    8212, "&#8212;" ], 
27244     [    8216,  "'" ],  
27245     [    8217, "'" ],  
27246     [    8220, '"' ],  
27247     [    8221, '"' ],  
27248     [    8226, "*" ],  
27249     [    8230, "..." ]
27250 ]; 
27251
27252     /*
27253  * - LGPL
27254  *
27255  * HtmlEditor
27256  * 
27257  */
27258
27259 /**
27260  * @class Roo.bootstrap.HtmlEditor
27261  * @extends Roo.bootstrap.TextArea
27262  * Bootstrap HtmlEditor class
27263
27264  * @constructor
27265  * Create a new HtmlEditor
27266  * @param {Object} config The config object
27267  */
27268
27269 Roo.bootstrap.HtmlEditor = function(config){
27270     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27271     if (!this.toolbars) {
27272         this.toolbars = [];
27273     }
27274     
27275     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27276     this.addEvents({
27277             /**
27278              * @event initialize
27279              * Fires when the editor is fully initialized (including the iframe)
27280              * @param {HtmlEditor} this
27281              */
27282             initialize: true,
27283             /**
27284              * @event activate
27285              * Fires when the editor is first receives the focus. Any insertion must wait
27286              * until after this event.
27287              * @param {HtmlEditor} this
27288              */
27289             activate: true,
27290              /**
27291              * @event beforesync
27292              * Fires before the textarea is updated with content from the editor iframe. Return false
27293              * to cancel the sync.
27294              * @param {HtmlEditor} this
27295              * @param {String} html
27296              */
27297             beforesync: true,
27298              /**
27299              * @event beforepush
27300              * Fires before the iframe editor is updated with content from the textarea. Return false
27301              * to cancel the push.
27302              * @param {HtmlEditor} this
27303              * @param {String} html
27304              */
27305             beforepush: true,
27306              /**
27307              * @event sync
27308              * Fires when the textarea is updated with content from the editor iframe.
27309              * @param {HtmlEditor} this
27310              * @param {String} html
27311              */
27312             sync: true,
27313              /**
27314              * @event push
27315              * Fires when the iframe editor is updated with content from the textarea.
27316              * @param {HtmlEditor} this
27317              * @param {String} html
27318              */
27319             push: true,
27320              /**
27321              * @event editmodechange
27322              * Fires when the editor switches edit modes
27323              * @param {HtmlEditor} this
27324              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27325              */
27326             editmodechange: true,
27327             /**
27328              * @event editorevent
27329              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27330              * @param {HtmlEditor} this
27331              */
27332             editorevent: true,
27333             /**
27334              * @event firstfocus
27335              * Fires when on first focus - needed by toolbars..
27336              * @param {HtmlEditor} this
27337              */
27338             firstfocus: true,
27339             /**
27340              * @event autosave
27341              * Auto save the htmlEditor value as a file into Events
27342              * @param {HtmlEditor} this
27343              */
27344             autosave: true,
27345             /**
27346              * @event savedpreview
27347              * preview the saved version of htmlEditor
27348              * @param {HtmlEditor} this
27349              */
27350             savedpreview: true
27351         });
27352 };
27353
27354
27355 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27356     
27357     
27358       /**
27359      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27360      */
27361     toolbars : false,
27362     
27363      /**
27364     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27365     */
27366     btns : [],
27367    
27368      /**
27369      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27370      *                        Roo.resizable.
27371      */
27372     resizable : false,
27373      /**
27374      * @cfg {Number} height (in pixels)
27375      */   
27376     height: 300,
27377    /**
27378      * @cfg {Number} width (in pixels)
27379      */   
27380     width: false,
27381     
27382     /**
27383      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27384      * 
27385      */
27386     stylesheets: false,
27387     
27388     // id of frame..
27389     frameId: false,
27390     
27391     // private properties
27392     validationEvent : false,
27393     deferHeight: true,
27394     initialized : false,
27395     activated : false,
27396     
27397     onFocus : Roo.emptyFn,
27398     iframePad:3,
27399     hideMode:'offsets',
27400     
27401     tbContainer : false,
27402     
27403     bodyCls : '',
27404     
27405     toolbarContainer :function() {
27406         return this.wrap.select('.x-html-editor-tb',true).first();
27407     },
27408
27409     /**
27410      * Protected method that will not generally be called directly. It
27411      * is called when the editor creates its toolbar. Override this method if you need to
27412      * add custom toolbar buttons.
27413      * @param {HtmlEditor} editor
27414      */
27415     createToolbar : function(){
27416         Roo.log('renewing');
27417         Roo.log("create toolbars");
27418         
27419         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27420         this.toolbars[0].render(this.toolbarContainer());
27421         
27422         return;
27423         
27424 //        if (!editor.toolbars || !editor.toolbars.length) {
27425 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27426 //        }
27427 //        
27428 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27429 //            editor.toolbars[i] = Roo.factory(
27430 //                    typeof(editor.toolbars[i]) == 'string' ?
27431 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27432 //                Roo.bootstrap.HtmlEditor);
27433 //            editor.toolbars[i].init(editor);
27434 //        }
27435     },
27436
27437      
27438     // private
27439     onRender : function(ct, position)
27440     {
27441        // Roo.log("Call onRender: " + this.xtype);
27442         var _t = this;
27443         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27444       
27445         this.wrap = this.inputEl().wrap({
27446             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27447         });
27448         
27449         this.editorcore.onRender(ct, position);
27450          
27451         if (this.resizable) {
27452             this.resizeEl = new Roo.Resizable(this.wrap, {
27453                 pinned : true,
27454                 wrap: true,
27455                 dynamic : true,
27456                 minHeight : this.height,
27457                 height: this.height,
27458                 handles : this.resizable,
27459                 width: this.width,
27460                 listeners : {
27461                     resize : function(r, w, h) {
27462                         _t.onResize(w,h); // -something
27463                     }
27464                 }
27465             });
27466             
27467         }
27468         this.createToolbar(this);
27469        
27470         
27471         if(!this.width && this.resizable){
27472             this.setSize(this.wrap.getSize());
27473         }
27474         if (this.resizeEl) {
27475             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27476             // should trigger onReize..
27477         }
27478         
27479     },
27480
27481     // private
27482     onResize : function(w, h)
27483     {
27484         Roo.log('resize: ' +w + ',' + h );
27485         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27486         var ew = false;
27487         var eh = false;
27488         
27489         if(this.inputEl() ){
27490             if(typeof w == 'number'){
27491                 var aw = w - this.wrap.getFrameWidth('lr');
27492                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27493                 ew = aw;
27494             }
27495             if(typeof h == 'number'){
27496                  var tbh = -11;  // fixme it needs to tool bar size!
27497                 for (var i =0; i < this.toolbars.length;i++) {
27498                     // fixme - ask toolbars for heights?
27499                     tbh += this.toolbars[i].el.getHeight();
27500                     //if (this.toolbars[i].footer) {
27501                     //    tbh += this.toolbars[i].footer.el.getHeight();
27502                     //}
27503                 }
27504               
27505                 
27506                 
27507                 
27508                 
27509                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27510                 ah -= 5; // knock a few pixes off for look..
27511                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27512                 var eh = ah;
27513             }
27514         }
27515         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27516         this.editorcore.onResize(ew,eh);
27517         
27518     },
27519
27520     /**
27521      * Toggles the editor between standard and source edit mode.
27522      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27523      */
27524     toggleSourceEdit : function(sourceEditMode)
27525     {
27526         this.editorcore.toggleSourceEdit(sourceEditMode);
27527         
27528         if(this.editorcore.sourceEditMode){
27529             Roo.log('editor - showing textarea');
27530             
27531 //            Roo.log('in');
27532 //            Roo.log(this.syncValue());
27533             this.syncValue();
27534             this.inputEl().removeClass(['hide', 'x-hidden']);
27535             this.inputEl().dom.removeAttribute('tabIndex');
27536             this.inputEl().focus();
27537         }else{
27538             Roo.log('editor - hiding textarea');
27539 //            Roo.log('out')
27540 //            Roo.log(this.pushValue()); 
27541             this.pushValue();
27542             
27543             this.inputEl().addClass(['hide', 'x-hidden']);
27544             this.inputEl().dom.setAttribute('tabIndex', -1);
27545             //this.deferFocus();
27546         }
27547          
27548         if(this.resizable){
27549             this.setSize(this.wrap.getSize());
27550         }
27551         
27552         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27553     },
27554  
27555     // private (for BoxComponent)
27556     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27557
27558     // private (for BoxComponent)
27559     getResizeEl : function(){
27560         return this.wrap;
27561     },
27562
27563     // private (for BoxComponent)
27564     getPositionEl : function(){
27565         return this.wrap;
27566     },
27567
27568     // private
27569     initEvents : function(){
27570         this.originalValue = this.getValue();
27571     },
27572
27573 //    /**
27574 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27575 //     * @method
27576 //     */
27577 //    markInvalid : Roo.emptyFn,
27578 //    /**
27579 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27580 //     * @method
27581 //     */
27582 //    clearInvalid : Roo.emptyFn,
27583
27584     setValue : function(v){
27585         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27586         this.editorcore.pushValue();
27587     },
27588
27589      
27590     // private
27591     deferFocus : function(){
27592         this.focus.defer(10, this);
27593     },
27594
27595     // doc'ed in Field
27596     focus : function(){
27597         this.editorcore.focus();
27598         
27599     },
27600       
27601
27602     // private
27603     onDestroy : function(){
27604         
27605         
27606         
27607         if(this.rendered){
27608             
27609             for (var i =0; i < this.toolbars.length;i++) {
27610                 // fixme - ask toolbars for heights?
27611                 this.toolbars[i].onDestroy();
27612             }
27613             
27614             this.wrap.dom.innerHTML = '';
27615             this.wrap.remove();
27616         }
27617     },
27618
27619     // private
27620     onFirstFocus : function(){
27621         //Roo.log("onFirstFocus");
27622         this.editorcore.onFirstFocus();
27623          for (var i =0; i < this.toolbars.length;i++) {
27624             this.toolbars[i].onFirstFocus();
27625         }
27626         
27627     },
27628     
27629     // private
27630     syncValue : function()
27631     {   
27632         this.editorcore.syncValue();
27633     },
27634     
27635     pushValue : function()
27636     {   
27637         this.editorcore.pushValue();
27638     }
27639      
27640     
27641     // hide stuff that is not compatible
27642     /**
27643      * @event blur
27644      * @hide
27645      */
27646     /**
27647      * @event change
27648      * @hide
27649      */
27650     /**
27651      * @event focus
27652      * @hide
27653      */
27654     /**
27655      * @event specialkey
27656      * @hide
27657      */
27658     /**
27659      * @cfg {String} fieldClass @hide
27660      */
27661     /**
27662      * @cfg {String} focusClass @hide
27663      */
27664     /**
27665      * @cfg {String} autoCreate @hide
27666      */
27667     /**
27668      * @cfg {String} inputType @hide
27669      */
27670      
27671     /**
27672      * @cfg {String} invalidText @hide
27673      */
27674     /**
27675      * @cfg {String} msgFx @hide
27676      */
27677     /**
27678      * @cfg {String} validateOnBlur @hide
27679      */
27680 });
27681  
27682     
27683    
27684    
27685    
27686       
27687 Roo.namespace('Roo.bootstrap.htmleditor');
27688 /**
27689  * @class Roo.bootstrap.HtmlEditorToolbar1
27690  * Basic Toolbar
27691  * 
27692  * @example
27693  * Usage:
27694  *
27695  new Roo.bootstrap.HtmlEditor({
27696     ....
27697     toolbars : [
27698         new Roo.bootstrap.HtmlEditorToolbar1({
27699             disable : { fonts: 1 , format: 1, ..., ... , ...],
27700             btns : [ .... ]
27701         })
27702     }
27703      
27704  * 
27705  * @cfg {Object} disable List of elements to disable..
27706  * @cfg {Array} btns List of additional buttons.
27707  * 
27708  * 
27709  * NEEDS Extra CSS? 
27710  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27711  */
27712  
27713 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27714 {
27715     
27716     Roo.apply(this, config);
27717     
27718     // default disabled, based on 'good practice'..
27719     this.disable = this.disable || {};
27720     Roo.applyIf(this.disable, {
27721         fontSize : true,
27722         colors : true,
27723         specialElements : true
27724     });
27725     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27726     
27727     this.editor = config.editor;
27728     this.editorcore = config.editor.editorcore;
27729     
27730     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27731     
27732     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27733     // dont call parent... till later.
27734 }
27735 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27736      
27737     bar : true,
27738     
27739     editor : false,
27740     editorcore : false,
27741     
27742     
27743     formats : [
27744         "p" ,  
27745         "h1","h2","h3","h4","h5","h6", 
27746         "pre", "code", 
27747         "abbr", "acronym", "address", "cite", "samp", "var",
27748         'div','span'
27749     ],
27750     
27751     onRender : function(ct, position)
27752     {
27753        // Roo.log("Call onRender: " + this.xtype);
27754         
27755        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27756        Roo.log(this.el);
27757        this.el.dom.style.marginBottom = '0';
27758        var _this = this;
27759        var editorcore = this.editorcore;
27760        var editor= this.editor;
27761        
27762        var children = [];
27763        var btn = function(id,cmd , toggle, handler, html){
27764        
27765             var  event = toggle ? 'toggle' : 'click';
27766        
27767             var a = {
27768                 size : 'sm',
27769                 xtype: 'Button',
27770                 xns: Roo.bootstrap,
27771                 //glyphicon : id,
27772                 fa: id,
27773                 cmd : id || cmd,
27774                 enableToggle:toggle !== false,
27775                 html : html || '',
27776                 pressed : toggle ? false : null,
27777                 listeners : {}
27778             };
27779             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27780                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27781             };
27782             children.push(a);
27783             return a;
27784        }
27785        
27786     //    var cb_box = function...
27787         
27788         var style = {
27789                 xtype: 'Button',
27790                 size : 'sm',
27791                 xns: Roo.bootstrap,
27792                 fa : 'font',
27793                 //html : 'submit'
27794                 menu : {
27795                     xtype: 'Menu',
27796                     xns: Roo.bootstrap,
27797                     items:  []
27798                 }
27799         };
27800         Roo.each(this.formats, function(f) {
27801             style.menu.items.push({
27802                 xtype :'MenuItem',
27803                 xns: Roo.bootstrap,
27804                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27805                 tagname : f,
27806                 listeners : {
27807                     click : function()
27808                     {
27809                         editorcore.insertTag(this.tagname);
27810                         editor.focus();
27811                     }
27812                 }
27813                 
27814             });
27815         });
27816         children.push(style);   
27817         
27818         btn('bold',false,true);
27819         btn('italic',false,true);
27820         btn('align-left', 'justifyleft',true);
27821         btn('align-center', 'justifycenter',true);
27822         btn('align-right' , 'justifyright',true);
27823         btn('link', false, false, function(btn) {
27824             //Roo.log("create link?");
27825             var url = prompt(this.createLinkText, this.defaultLinkValue);
27826             if(url && url != 'http:/'+'/'){
27827                 this.editorcore.relayCmd('createlink', url);
27828             }
27829         }),
27830         btn('list','insertunorderedlist',true);
27831         btn('pencil', false,true, function(btn){
27832                 Roo.log(this);
27833                 this.toggleSourceEdit(btn.pressed);
27834         });
27835         
27836         if (this.editor.btns.length > 0) {
27837             for (var i = 0; i<this.editor.btns.length; i++) {
27838                 children.push(this.editor.btns[i]);
27839             }
27840         }
27841         
27842         /*
27843         var cog = {
27844                 xtype: 'Button',
27845                 size : 'sm',
27846                 xns: Roo.bootstrap,
27847                 glyphicon : 'cog',
27848                 //html : 'submit'
27849                 menu : {
27850                     xtype: 'Menu',
27851                     xns: Roo.bootstrap,
27852                     items:  []
27853                 }
27854         };
27855         
27856         cog.menu.items.push({
27857             xtype :'MenuItem',
27858             xns: Roo.bootstrap,
27859             html : Clean styles,
27860             tagname : f,
27861             listeners : {
27862                 click : function()
27863                 {
27864                     editorcore.insertTag(this.tagname);
27865                     editor.focus();
27866                 }
27867             }
27868             
27869         });
27870        */
27871         
27872          
27873        this.xtype = 'NavSimplebar';
27874         
27875         for(var i=0;i< children.length;i++) {
27876             
27877             this.buttons.add(this.addxtypeChild(children[i]));
27878             
27879         }
27880         
27881         editor.on('editorevent', this.updateToolbar, this);
27882     },
27883     onBtnClick : function(id)
27884     {
27885        this.editorcore.relayCmd(id);
27886        this.editorcore.focus();
27887     },
27888     
27889     /**
27890      * Protected method that will not generally be called directly. It triggers
27891      * a toolbar update by reading the markup state of the current selection in the editor.
27892      */
27893     updateToolbar: function(){
27894
27895         if(!this.editorcore.activated){
27896             this.editor.onFirstFocus(); // is this neeed?
27897             return;
27898         }
27899
27900         var btns = this.buttons; 
27901         var doc = this.editorcore.doc;
27902         btns.get('bold').setActive(doc.queryCommandState('bold'));
27903         btns.get('italic').setActive(doc.queryCommandState('italic'));
27904         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27905         
27906         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27907         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27908         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27909         
27910         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27911         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27912          /*
27913         
27914         var ans = this.editorcore.getAllAncestors();
27915         if (this.formatCombo) {
27916             
27917             
27918             var store = this.formatCombo.store;
27919             this.formatCombo.setValue("");
27920             for (var i =0; i < ans.length;i++) {
27921                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27922                     // select it..
27923                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27924                     break;
27925                 }
27926             }
27927         }
27928         
27929         
27930         
27931         // hides menus... - so this cant be on a menu...
27932         Roo.bootstrap.MenuMgr.hideAll();
27933         */
27934         Roo.bootstrap.MenuMgr.hideAll();
27935         //this.editorsyncValue();
27936     },
27937     onFirstFocus: function() {
27938         this.buttons.each(function(item){
27939            item.enable();
27940         });
27941     },
27942     toggleSourceEdit : function(sourceEditMode){
27943         
27944           
27945         if(sourceEditMode){
27946             Roo.log("disabling buttons");
27947            this.buttons.each( function(item){
27948                 if(item.cmd != 'pencil'){
27949                     item.disable();
27950                 }
27951             });
27952           
27953         }else{
27954             Roo.log("enabling buttons");
27955             if(this.editorcore.initialized){
27956                 this.buttons.each( function(item){
27957                     item.enable();
27958                 });
27959             }
27960             
27961         }
27962         Roo.log("calling toggole on editor");
27963         // tell the editor that it's been pressed..
27964         this.editor.toggleSourceEdit(sourceEditMode);
27965        
27966     }
27967 });
27968
27969
27970
27971
27972  
27973 /*
27974  * - LGPL
27975  */
27976
27977 /**
27978  * @class Roo.bootstrap.Markdown
27979  * @extends Roo.bootstrap.TextArea
27980  * Bootstrap Showdown editable area
27981  * @cfg {string} content
27982  * 
27983  * @constructor
27984  * Create a new Showdown
27985  */
27986
27987 Roo.bootstrap.Markdown = function(config){
27988     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27989    
27990 };
27991
27992 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27993     
27994     editing :false,
27995     
27996     initEvents : function()
27997     {
27998         
27999         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
28000         this.markdownEl = this.el.createChild({
28001             cls : 'roo-markdown-area'
28002         });
28003         this.inputEl().addClass('d-none');
28004         if (this.getValue() == '') {
28005             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28006             
28007         } else {
28008             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28009         }
28010         this.markdownEl.on('click', this.toggleTextEdit, this);
28011         this.on('blur', this.toggleTextEdit, this);
28012         this.on('specialkey', this.resizeTextArea, this);
28013     },
28014     
28015     toggleTextEdit : function()
28016     {
28017         var sh = this.markdownEl.getHeight();
28018         this.inputEl().addClass('d-none');
28019         this.markdownEl.addClass('d-none');
28020         if (!this.editing) {
28021             // show editor?
28022             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28023             this.inputEl().removeClass('d-none');
28024             this.inputEl().focus();
28025             this.editing = true;
28026             return;
28027         }
28028         // show showdown...
28029         this.updateMarkdown();
28030         this.markdownEl.removeClass('d-none');
28031         this.editing = false;
28032         return;
28033     },
28034     updateMarkdown : function()
28035     {
28036         if (this.getValue() == '') {
28037             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28038             return;
28039         }
28040  
28041         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28042     },
28043     
28044     resizeTextArea: function () {
28045         
28046         var sh = 100;
28047         Roo.log([sh, this.getValue().split("\n").length * 30]);
28048         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28049     },
28050     setValue : function(val)
28051     {
28052         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28053         if (!this.editing) {
28054             this.updateMarkdown();
28055         }
28056         
28057     },
28058     focus : function()
28059     {
28060         if (!this.editing) {
28061             this.toggleTextEdit();
28062         }
28063         
28064     }
28065
28066
28067 });/*
28068  * Based on:
28069  * Ext JS Library 1.1.1
28070  * Copyright(c) 2006-2007, Ext JS, LLC.
28071  *
28072  * Originally Released Under LGPL - original licence link has changed is not relivant.
28073  *
28074  * Fork - LGPL
28075  * <script type="text/javascript">
28076  */
28077  
28078 /**
28079  * @class Roo.bootstrap.PagingToolbar
28080  * @extends Roo.bootstrap.NavSimplebar
28081  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28082  * @constructor
28083  * Create a new PagingToolbar
28084  * @param {Object} config The config object
28085  * @param {Roo.data.Store} store
28086  */
28087 Roo.bootstrap.PagingToolbar = function(config)
28088 {
28089     // old args format still supported... - xtype is prefered..
28090         // created from xtype...
28091     
28092     this.ds = config.dataSource;
28093     
28094     if (config.store && !this.ds) {
28095         this.store= Roo.factory(config.store, Roo.data);
28096         this.ds = this.store;
28097         this.ds.xmodule = this.xmodule || false;
28098     }
28099     
28100     this.toolbarItems = [];
28101     if (config.items) {
28102         this.toolbarItems = config.items;
28103     }
28104     
28105     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28106     
28107     this.cursor = 0;
28108     
28109     if (this.ds) { 
28110         this.bind(this.ds);
28111     }
28112     
28113     if (Roo.bootstrap.version == 4) {
28114         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28115     } else {
28116         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28117     }
28118     
28119 };
28120
28121 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28122     /**
28123      * @cfg {Roo.data.Store} dataSource
28124      * The underlying data store providing the paged data
28125      */
28126     /**
28127      * @cfg {String/HTMLElement/Element} container
28128      * container The id or element that will contain the toolbar
28129      */
28130     /**
28131      * @cfg {Boolean} displayInfo
28132      * True to display the displayMsg (defaults to false)
28133      */
28134     /**
28135      * @cfg {Number} pageSize
28136      * The number of records to display per page (defaults to 20)
28137      */
28138     pageSize: 20,
28139     /**
28140      * @cfg {String} displayMsg
28141      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28142      */
28143     displayMsg : 'Displaying {0} - {1} of {2}',
28144     /**
28145      * @cfg {String} emptyMsg
28146      * The message to display when no records are found (defaults to "No data to display")
28147      */
28148     emptyMsg : 'No data to display',
28149     /**
28150      * Customizable piece of the default paging text (defaults to "Page")
28151      * @type String
28152      */
28153     beforePageText : "Page",
28154     /**
28155      * Customizable piece of the default paging text (defaults to "of %0")
28156      * @type String
28157      */
28158     afterPageText : "of {0}",
28159     /**
28160      * Customizable piece of the default paging text (defaults to "First Page")
28161      * @type String
28162      */
28163     firstText : "First Page",
28164     /**
28165      * Customizable piece of the default paging text (defaults to "Previous Page")
28166      * @type String
28167      */
28168     prevText : "Previous Page",
28169     /**
28170      * Customizable piece of the default paging text (defaults to "Next Page")
28171      * @type String
28172      */
28173     nextText : "Next Page",
28174     /**
28175      * Customizable piece of the default paging text (defaults to "Last Page")
28176      * @type String
28177      */
28178     lastText : "Last Page",
28179     /**
28180      * Customizable piece of the default paging text (defaults to "Refresh")
28181      * @type String
28182      */
28183     refreshText : "Refresh",
28184
28185     buttons : false,
28186     // private
28187     onRender : function(ct, position) 
28188     {
28189         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28190         this.navgroup.parentId = this.id;
28191         this.navgroup.onRender(this.el, null);
28192         // add the buttons to the navgroup
28193         
28194         if(this.displayInfo){
28195             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28196             this.displayEl = this.el.select('.x-paging-info', true).first();
28197 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28198 //            this.displayEl = navel.el.select('span',true).first();
28199         }
28200         
28201         var _this = this;
28202         
28203         if(this.buttons){
28204             Roo.each(_this.buttons, function(e){ // this might need to use render????
28205                Roo.factory(e).render(_this.el);
28206             });
28207         }
28208             
28209         Roo.each(_this.toolbarItems, function(e) {
28210             _this.navgroup.addItem(e);
28211         });
28212         
28213         
28214         this.first = this.navgroup.addItem({
28215             tooltip: this.firstText,
28216             cls: "prev btn-outline-secondary",
28217             html : ' <i class="fa fa-step-backward"></i>',
28218             disabled: true,
28219             preventDefault: true,
28220             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28221         });
28222         
28223         this.prev =  this.navgroup.addItem({
28224             tooltip: this.prevText,
28225             cls: "prev btn-outline-secondary",
28226             html : ' <i class="fa fa-backward"></i>',
28227             disabled: true,
28228             preventDefault: true,
28229             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28230         });
28231     //this.addSeparator();
28232         
28233         
28234         var field = this.navgroup.addItem( {
28235             tagtype : 'span',
28236             cls : 'x-paging-position  btn-outline-secondary',
28237              disabled: true,
28238             html : this.beforePageText  +
28239                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28240                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28241          } ); //?? escaped?
28242         
28243         this.field = field.el.select('input', true).first();
28244         this.field.on("keydown", this.onPagingKeydown, this);
28245         this.field.on("focus", function(){this.dom.select();});
28246     
28247     
28248         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28249         //this.field.setHeight(18);
28250         //this.addSeparator();
28251         this.next = this.navgroup.addItem({
28252             tooltip: this.nextText,
28253             cls: "next btn-outline-secondary",
28254             html : ' <i class="fa fa-forward"></i>',
28255             disabled: true,
28256             preventDefault: true,
28257             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28258         });
28259         this.last = this.navgroup.addItem({
28260             tooltip: this.lastText,
28261             html : ' <i class="fa fa-step-forward"></i>',
28262             cls: "next btn-outline-secondary",
28263             disabled: true,
28264             preventDefault: true,
28265             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28266         });
28267     //this.addSeparator();
28268         this.loading = this.navgroup.addItem({
28269             tooltip: this.refreshText,
28270             cls: "btn-outline-secondary",
28271             html : ' <i class="fa fa-refresh"></i>',
28272             preventDefault: true,
28273             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28274         });
28275         
28276     },
28277
28278     // private
28279     updateInfo : function(){
28280         if(this.displayEl){
28281             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28282             var msg = count == 0 ?
28283                 this.emptyMsg :
28284                 String.format(
28285                     this.displayMsg,
28286                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28287                 );
28288             this.displayEl.update(msg);
28289         }
28290     },
28291
28292     // private
28293     onLoad : function(ds, r, o)
28294     {
28295         this.cursor = o.params && o.params.start ? o.params.start : 0;
28296         
28297         var d = this.getPageData(),
28298             ap = d.activePage,
28299             ps = d.pages;
28300         
28301         
28302         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28303         this.field.dom.value = ap;
28304         this.first.setDisabled(ap == 1);
28305         this.prev.setDisabled(ap == 1);
28306         this.next.setDisabled(ap == ps);
28307         this.last.setDisabled(ap == ps);
28308         this.loading.enable();
28309         this.updateInfo();
28310     },
28311
28312     // private
28313     getPageData : function(){
28314         var total = this.ds.getTotalCount();
28315         return {
28316             total : total,
28317             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28318             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28319         };
28320     },
28321
28322     // private
28323     onLoadError : function(){
28324         this.loading.enable();
28325     },
28326
28327     // private
28328     onPagingKeydown : function(e){
28329         var k = e.getKey();
28330         var d = this.getPageData();
28331         if(k == e.RETURN){
28332             var v = this.field.dom.value, pageNum;
28333             if(!v || isNaN(pageNum = parseInt(v, 10))){
28334                 this.field.dom.value = d.activePage;
28335                 return;
28336             }
28337             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28338             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28339             e.stopEvent();
28340         }
28341         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))
28342         {
28343           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28344           this.field.dom.value = pageNum;
28345           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28346           e.stopEvent();
28347         }
28348         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28349         {
28350           var v = this.field.dom.value, pageNum; 
28351           var increment = (e.shiftKey) ? 10 : 1;
28352           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28353                 increment *= -1;
28354           }
28355           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28356             this.field.dom.value = d.activePage;
28357             return;
28358           }
28359           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28360           {
28361             this.field.dom.value = parseInt(v, 10) + increment;
28362             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28363             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28364           }
28365           e.stopEvent();
28366         }
28367     },
28368
28369     // private
28370     beforeLoad : function(){
28371         if(this.loading){
28372             this.loading.disable();
28373         }
28374     },
28375
28376     // private
28377     onClick : function(which){
28378         
28379         var ds = this.ds;
28380         if (!ds) {
28381             return;
28382         }
28383         
28384         switch(which){
28385             case "first":
28386                 ds.load({params:{start: 0, limit: this.pageSize}});
28387             break;
28388             case "prev":
28389                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28390             break;
28391             case "next":
28392                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28393             break;
28394             case "last":
28395                 var total = ds.getTotalCount();
28396                 var extra = total % this.pageSize;
28397                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28398                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28399             break;
28400             case "refresh":
28401                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28402             break;
28403         }
28404     },
28405
28406     /**
28407      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28408      * @param {Roo.data.Store} store The data store to unbind
28409      */
28410     unbind : function(ds){
28411         ds.un("beforeload", this.beforeLoad, this);
28412         ds.un("load", this.onLoad, this);
28413         ds.un("loadexception", this.onLoadError, this);
28414         ds.un("remove", this.updateInfo, this);
28415         ds.un("add", this.updateInfo, this);
28416         this.ds = undefined;
28417     },
28418
28419     /**
28420      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28421      * @param {Roo.data.Store} store The data store to bind
28422      */
28423     bind : function(ds){
28424         ds.on("beforeload", this.beforeLoad, this);
28425         ds.on("load", this.onLoad, this);
28426         ds.on("loadexception", this.onLoadError, this);
28427         ds.on("remove", this.updateInfo, this);
28428         ds.on("add", this.updateInfo, this);
28429         this.ds = ds;
28430     }
28431 });/*
28432  * - LGPL
28433  *
28434  * element
28435  * 
28436  */
28437
28438 /**
28439  * @class Roo.bootstrap.MessageBar
28440  * @extends Roo.bootstrap.Component
28441  * Bootstrap MessageBar class
28442  * @cfg {String} html contents of the MessageBar
28443  * @cfg {String} weight (info | success | warning | danger) default info
28444  * @cfg {String} beforeClass insert the bar before the given class
28445  * @cfg {Boolean} closable (true | false) default false
28446  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28447  * 
28448  * @constructor
28449  * Create a new Element
28450  * @param {Object} config The config object
28451  */
28452
28453 Roo.bootstrap.MessageBar = function(config){
28454     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28455 };
28456
28457 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28458     
28459     html: '',
28460     weight: 'info',
28461     closable: false,
28462     fixed: false,
28463     beforeClass: 'bootstrap-sticky-wrap',
28464     
28465     getAutoCreate : function(){
28466         
28467         var cfg = {
28468             tag: 'div',
28469             cls: 'alert alert-dismissable alert-' + this.weight,
28470             cn: [
28471                 {
28472                     tag: 'span',
28473                     cls: 'message',
28474                     html: this.html || ''
28475                 }
28476             ]
28477         };
28478         
28479         if(this.fixed){
28480             cfg.cls += ' alert-messages-fixed';
28481         }
28482         
28483         if(this.closable){
28484             cfg.cn.push({
28485                 tag: 'button',
28486                 cls: 'close',
28487                 html: 'x'
28488             });
28489         }
28490         
28491         return cfg;
28492     },
28493     
28494     onRender : function(ct, position)
28495     {
28496         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28497         
28498         if(!this.el){
28499             var cfg = Roo.apply({},  this.getAutoCreate());
28500             cfg.id = Roo.id();
28501             
28502             if (this.cls) {
28503                 cfg.cls += ' ' + this.cls;
28504             }
28505             if (this.style) {
28506                 cfg.style = this.style;
28507             }
28508             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28509             
28510             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28511         }
28512         
28513         this.el.select('>button.close').on('click', this.hide, this);
28514         
28515     },
28516     
28517     show : function()
28518     {
28519         if (!this.rendered) {
28520             this.render();
28521         }
28522         
28523         this.el.show();
28524         
28525         this.fireEvent('show', this);
28526         
28527     },
28528     
28529     hide : function()
28530     {
28531         if (!this.rendered) {
28532             this.render();
28533         }
28534         
28535         this.el.hide();
28536         
28537         this.fireEvent('hide', this);
28538     },
28539     
28540     update : function()
28541     {
28542 //        var e = this.el.dom.firstChild;
28543 //        
28544 //        if(this.closable){
28545 //            e = e.nextSibling;
28546 //        }
28547 //        
28548 //        e.data = this.html || '';
28549
28550         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28551     }
28552    
28553 });
28554
28555  
28556
28557      /*
28558  * - LGPL
28559  *
28560  * Graph
28561  * 
28562  */
28563
28564
28565 /**
28566  * @class Roo.bootstrap.Graph
28567  * @extends Roo.bootstrap.Component
28568  * Bootstrap Graph class
28569 > Prameters
28570  -sm {number} sm 4
28571  -md {number} md 5
28572  @cfg {String} graphtype  bar | vbar | pie
28573  @cfg {number} g_x coodinator | centre x (pie)
28574  @cfg {number} g_y coodinator | centre y (pie)
28575  @cfg {number} g_r radius (pie)
28576  @cfg {number} g_height height of the chart (respected by all elements in the set)
28577  @cfg {number} g_width width of the chart (respected by all elements in the set)
28578  @cfg {Object} title The title of the chart
28579     
28580  -{Array}  values
28581  -opts (object) options for the chart 
28582      o {
28583      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28584      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28585      o vgutter (number)
28586      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.
28587      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28588      o to
28589      o stretch (boolean)
28590      o }
28591  -opts (object) options for the pie
28592      o{
28593      o cut
28594      o startAngle (number)
28595      o endAngle (number)
28596      } 
28597  *
28598  * @constructor
28599  * Create a new Input
28600  * @param {Object} config The config object
28601  */
28602
28603 Roo.bootstrap.Graph = function(config){
28604     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28605     
28606     this.addEvents({
28607         // img events
28608         /**
28609          * @event click
28610          * The img click event for the img.
28611          * @param {Roo.EventObject} e
28612          */
28613         "click" : true
28614     });
28615 };
28616
28617 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28618     
28619     sm: 4,
28620     md: 5,
28621     graphtype: 'bar',
28622     g_height: 250,
28623     g_width: 400,
28624     g_x: 50,
28625     g_y: 50,
28626     g_r: 30,
28627     opts:{
28628         //g_colors: this.colors,
28629         g_type: 'soft',
28630         g_gutter: '20%'
28631
28632     },
28633     title : false,
28634
28635     getAutoCreate : function(){
28636         
28637         var cfg = {
28638             tag: 'div',
28639             html : null
28640         };
28641         
28642         
28643         return  cfg;
28644     },
28645
28646     onRender : function(ct,position){
28647         
28648         
28649         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28650         
28651         if (typeof(Raphael) == 'undefined') {
28652             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28653             return;
28654         }
28655         
28656         this.raphael = Raphael(this.el.dom);
28657         
28658                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28659                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28660                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28661                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28662                 /*
28663                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28664                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28665                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28666                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28667                 
28668                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28669                 r.barchart(330, 10, 300, 220, data1);
28670                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28671                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28672                 */
28673                 
28674                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28675                 // r.barchart(30, 30, 560, 250,  xdata, {
28676                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28677                 //     axis : "0 0 1 1",
28678                 //     axisxlabels :  xdata
28679                 //     //yvalues : cols,
28680                    
28681                 // });
28682 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28683 //        
28684 //        this.load(null,xdata,{
28685 //                axis : "0 0 1 1",
28686 //                axisxlabels :  xdata
28687 //                });
28688
28689     },
28690
28691     load : function(graphtype,xdata,opts)
28692     {
28693         this.raphael.clear();
28694         if(!graphtype) {
28695             graphtype = this.graphtype;
28696         }
28697         if(!opts){
28698             opts = this.opts;
28699         }
28700         var r = this.raphael,
28701             fin = function () {
28702                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28703             },
28704             fout = function () {
28705                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28706             },
28707             pfin = function() {
28708                 this.sector.stop();
28709                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28710
28711                 if (this.label) {
28712                     this.label[0].stop();
28713                     this.label[0].attr({ r: 7.5 });
28714                     this.label[1].attr({ "font-weight": 800 });
28715                 }
28716             },
28717             pfout = function() {
28718                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28719
28720                 if (this.label) {
28721                     this.label[0].animate({ r: 5 }, 500, "bounce");
28722                     this.label[1].attr({ "font-weight": 400 });
28723                 }
28724             };
28725
28726         switch(graphtype){
28727             case 'bar':
28728                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28729                 break;
28730             case 'hbar':
28731                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28732                 break;
28733             case 'pie':
28734 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28735 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28736 //            
28737                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28738                 
28739                 break;
28740
28741         }
28742         
28743         if(this.title){
28744             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28745         }
28746         
28747     },
28748     
28749     setTitle: function(o)
28750     {
28751         this.title = o;
28752     },
28753     
28754     initEvents: function() {
28755         
28756         if(!this.href){
28757             this.el.on('click', this.onClick, this);
28758         }
28759     },
28760     
28761     onClick : function(e)
28762     {
28763         Roo.log('img onclick');
28764         this.fireEvent('click', this, e);
28765     }
28766    
28767 });
28768
28769  
28770 /*
28771  * - LGPL
28772  *
28773  * numberBox
28774  * 
28775  */
28776 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28777
28778 /**
28779  * @class Roo.bootstrap.dash.NumberBox
28780  * @extends Roo.bootstrap.Component
28781  * Bootstrap NumberBox class
28782  * @cfg {String} headline Box headline
28783  * @cfg {String} content Box content
28784  * @cfg {String} icon Box icon
28785  * @cfg {String} footer Footer text
28786  * @cfg {String} fhref Footer href
28787  * 
28788  * @constructor
28789  * Create a new NumberBox
28790  * @param {Object} config The config object
28791  */
28792
28793
28794 Roo.bootstrap.dash.NumberBox = function(config){
28795     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28796     
28797 };
28798
28799 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28800     
28801     headline : '',
28802     content : '',
28803     icon : '',
28804     footer : '',
28805     fhref : '',
28806     ficon : '',
28807     
28808     getAutoCreate : function(){
28809         
28810         var cfg = {
28811             tag : 'div',
28812             cls : 'small-box ',
28813             cn : [
28814                 {
28815                     tag : 'div',
28816                     cls : 'inner',
28817                     cn :[
28818                         {
28819                             tag : 'h3',
28820                             cls : 'roo-headline',
28821                             html : this.headline
28822                         },
28823                         {
28824                             tag : 'p',
28825                             cls : 'roo-content',
28826                             html : this.content
28827                         }
28828                     ]
28829                 }
28830             ]
28831         };
28832         
28833         if(this.icon){
28834             cfg.cn.push({
28835                 tag : 'div',
28836                 cls : 'icon',
28837                 cn :[
28838                     {
28839                         tag : 'i',
28840                         cls : 'ion ' + this.icon
28841                     }
28842                 ]
28843             });
28844         }
28845         
28846         if(this.footer){
28847             var footer = {
28848                 tag : 'a',
28849                 cls : 'small-box-footer',
28850                 href : this.fhref || '#',
28851                 html : this.footer
28852             };
28853             
28854             cfg.cn.push(footer);
28855             
28856         }
28857         
28858         return  cfg;
28859     },
28860
28861     onRender : function(ct,position){
28862         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28863
28864
28865        
28866                 
28867     },
28868
28869     setHeadline: function (value)
28870     {
28871         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28872     },
28873     
28874     setFooter: function (value, href)
28875     {
28876         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28877         
28878         if(href){
28879             this.el.select('a.small-box-footer',true).first().attr('href', href);
28880         }
28881         
28882     },
28883
28884     setContent: function (value)
28885     {
28886         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28887     },
28888
28889     initEvents: function() 
28890     {   
28891         
28892     }
28893     
28894 });
28895
28896  
28897 /*
28898  * - LGPL
28899  *
28900  * TabBox
28901  * 
28902  */
28903 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28904
28905 /**
28906  * @class Roo.bootstrap.dash.TabBox
28907  * @extends Roo.bootstrap.Component
28908  * Bootstrap TabBox class
28909  * @cfg {String} title Title of the TabBox
28910  * @cfg {String} icon Icon of the TabBox
28911  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28912  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28913  * 
28914  * @constructor
28915  * Create a new TabBox
28916  * @param {Object} config The config object
28917  */
28918
28919
28920 Roo.bootstrap.dash.TabBox = function(config){
28921     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28922     this.addEvents({
28923         // raw events
28924         /**
28925          * @event addpane
28926          * When a pane is added
28927          * @param {Roo.bootstrap.dash.TabPane} pane
28928          */
28929         "addpane" : true,
28930         /**
28931          * @event activatepane
28932          * When a pane is activated
28933          * @param {Roo.bootstrap.dash.TabPane} pane
28934          */
28935         "activatepane" : true
28936         
28937          
28938     });
28939     
28940     this.panes = [];
28941 };
28942
28943 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28944
28945     title : '',
28946     icon : false,
28947     showtabs : true,
28948     tabScrollable : false,
28949     
28950     getChildContainer : function()
28951     {
28952         return this.el.select('.tab-content', true).first();
28953     },
28954     
28955     getAutoCreate : function(){
28956         
28957         var header = {
28958             tag: 'li',
28959             cls: 'pull-left header',
28960             html: this.title,
28961             cn : []
28962         };
28963         
28964         if(this.icon){
28965             header.cn.push({
28966                 tag: 'i',
28967                 cls: 'fa ' + this.icon
28968             });
28969         }
28970         
28971         var h = {
28972             tag: 'ul',
28973             cls: 'nav nav-tabs pull-right',
28974             cn: [
28975                 header
28976             ]
28977         };
28978         
28979         if(this.tabScrollable){
28980             h = {
28981                 tag: 'div',
28982                 cls: 'tab-header',
28983                 cn: [
28984                     {
28985                         tag: 'ul',
28986                         cls: 'nav nav-tabs pull-right',
28987                         cn: [
28988                             header
28989                         ]
28990                     }
28991                 ]
28992             };
28993         }
28994         
28995         var cfg = {
28996             tag: 'div',
28997             cls: 'nav-tabs-custom',
28998             cn: [
28999                 h,
29000                 {
29001                     tag: 'div',
29002                     cls: 'tab-content no-padding',
29003                     cn: []
29004                 }
29005             ]
29006         };
29007
29008         return  cfg;
29009     },
29010     initEvents : function()
29011     {
29012         //Roo.log('add add pane handler');
29013         this.on('addpane', this.onAddPane, this);
29014     },
29015      /**
29016      * Updates the box title
29017      * @param {String} html to set the title to.
29018      */
29019     setTitle : function(value)
29020     {
29021         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29022     },
29023     onAddPane : function(pane)
29024     {
29025         this.panes.push(pane);
29026         //Roo.log('addpane');
29027         //Roo.log(pane);
29028         // tabs are rendere left to right..
29029         if(!this.showtabs){
29030             return;
29031         }
29032         
29033         var ctr = this.el.select('.nav-tabs', true).first();
29034          
29035          
29036         var existing = ctr.select('.nav-tab',true);
29037         var qty = existing.getCount();;
29038         
29039         
29040         var tab = ctr.createChild({
29041             tag : 'li',
29042             cls : 'nav-tab' + (qty ? '' : ' active'),
29043             cn : [
29044                 {
29045                     tag : 'a',
29046                     href:'#',
29047                     html : pane.title
29048                 }
29049             ]
29050         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29051         pane.tab = tab;
29052         
29053         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29054         if (!qty) {
29055             pane.el.addClass('active');
29056         }
29057         
29058                 
29059     },
29060     onTabClick : function(ev,un,ob,pane)
29061     {
29062         //Roo.log('tab - prev default');
29063         ev.preventDefault();
29064         
29065         
29066         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29067         pane.tab.addClass('active');
29068         //Roo.log(pane.title);
29069         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29070         // technically we should have a deactivate event.. but maybe add later.
29071         // and it should not de-activate the selected tab...
29072         this.fireEvent('activatepane', pane);
29073         pane.el.addClass('active');
29074         pane.fireEvent('activate');
29075         
29076         
29077     },
29078     
29079     getActivePane : function()
29080     {
29081         var r = false;
29082         Roo.each(this.panes, function(p) {
29083             if(p.el.hasClass('active')){
29084                 r = p;
29085                 return false;
29086             }
29087             
29088             return;
29089         });
29090         
29091         return r;
29092     }
29093     
29094     
29095 });
29096
29097  
29098 /*
29099  * - LGPL
29100  *
29101  * Tab pane
29102  * 
29103  */
29104 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29105 /**
29106  * @class Roo.bootstrap.TabPane
29107  * @extends Roo.bootstrap.Component
29108  * Bootstrap TabPane class
29109  * @cfg {Boolean} active (false | true) Default false
29110  * @cfg {String} title title of panel
29111
29112  * 
29113  * @constructor
29114  * Create a new TabPane
29115  * @param {Object} config The config object
29116  */
29117
29118 Roo.bootstrap.dash.TabPane = function(config){
29119     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29120     
29121     this.addEvents({
29122         // raw events
29123         /**
29124          * @event activate
29125          * When a pane is activated
29126          * @param {Roo.bootstrap.dash.TabPane} pane
29127          */
29128         "activate" : true
29129          
29130     });
29131 };
29132
29133 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29134     
29135     active : false,
29136     title : '',
29137     
29138     // the tabBox that this is attached to.
29139     tab : false,
29140      
29141     getAutoCreate : function() 
29142     {
29143         var cfg = {
29144             tag: 'div',
29145             cls: 'tab-pane'
29146         };
29147         
29148         if(this.active){
29149             cfg.cls += ' active';
29150         }
29151         
29152         return cfg;
29153     },
29154     initEvents  : function()
29155     {
29156         //Roo.log('trigger add pane handler');
29157         this.parent().fireEvent('addpane', this)
29158     },
29159     
29160      /**
29161      * Updates the tab title 
29162      * @param {String} html to set the title to.
29163      */
29164     setTitle: function(str)
29165     {
29166         if (!this.tab) {
29167             return;
29168         }
29169         this.title = str;
29170         this.tab.select('a', true).first().dom.innerHTML = str;
29171         
29172     }
29173     
29174     
29175     
29176 });
29177
29178  
29179
29180
29181  /*
29182  * - LGPL
29183  *
29184  * menu
29185  * 
29186  */
29187 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29188
29189 /**
29190  * @class Roo.bootstrap.menu.Menu
29191  * @extends Roo.bootstrap.Component
29192  * Bootstrap Menu class - container for Menu
29193  * @cfg {String} html Text of the menu
29194  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29195  * @cfg {String} icon Font awesome icon
29196  * @cfg {String} pos Menu align to (top | bottom) default bottom
29197  * 
29198  * 
29199  * @constructor
29200  * Create a new Menu
29201  * @param {Object} config The config object
29202  */
29203
29204
29205 Roo.bootstrap.menu.Menu = function(config){
29206     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29207     
29208     this.addEvents({
29209         /**
29210          * @event beforeshow
29211          * Fires before this menu is displayed
29212          * @param {Roo.bootstrap.menu.Menu} this
29213          */
29214         beforeshow : true,
29215         /**
29216          * @event beforehide
29217          * Fires before this menu is hidden
29218          * @param {Roo.bootstrap.menu.Menu} this
29219          */
29220         beforehide : true,
29221         /**
29222          * @event show
29223          * Fires after this menu is displayed
29224          * @param {Roo.bootstrap.menu.Menu} this
29225          */
29226         show : true,
29227         /**
29228          * @event hide
29229          * Fires after this menu is hidden
29230          * @param {Roo.bootstrap.menu.Menu} this
29231          */
29232         hide : true,
29233         /**
29234          * @event click
29235          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29236          * @param {Roo.bootstrap.menu.Menu} this
29237          * @param {Roo.EventObject} e
29238          */
29239         click : true
29240     });
29241     
29242 };
29243
29244 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29245     
29246     submenu : false,
29247     html : '',
29248     weight : 'default',
29249     icon : false,
29250     pos : 'bottom',
29251     
29252     
29253     getChildContainer : function() {
29254         if(this.isSubMenu){
29255             return this.el;
29256         }
29257         
29258         return this.el.select('ul.dropdown-menu', true).first();  
29259     },
29260     
29261     getAutoCreate : function()
29262     {
29263         var text = [
29264             {
29265                 tag : 'span',
29266                 cls : 'roo-menu-text',
29267                 html : this.html
29268             }
29269         ];
29270         
29271         if(this.icon){
29272             text.unshift({
29273                 tag : 'i',
29274                 cls : 'fa ' + this.icon
29275             })
29276         }
29277         
29278         
29279         var cfg = {
29280             tag : 'div',
29281             cls : 'btn-group',
29282             cn : [
29283                 {
29284                     tag : 'button',
29285                     cls : 'dropdown-button btn btn-' + this.weight,
29286                     cn : text
29287                 },
29288                 {
29289                     tag : 'button',
29290                     cls : 'dropdown-toggle btn btn-' + this.weight,
29291                     cn : [
29292                         {
29293                             tag : 'span',
29294                             cls : 'caret'
29295                         }
29296                     ]
29297                 },
29298                 {
29299                     tag : 'ul',
29300                     cls : 'dropdown-menu'
29301                 }
29302             ]
29303             
29304         };
29305         
29306         if(this.pos == 'top'){
29307             cfg.cls += ' dropup';
29308         }
29309         
29310         if(this.isSubMenu){
29311             cfg = {
29312                 tag : 'ul',
29313                 cls : 'dropdown-menu'
29314             }
29315         }
29316         
29317         return cfg;
29318     },
29319     
29320     onRender : function(ct, position)
29321     {
29322         this.isSubMenu = ct.hasClass('dropdown-submenu');
29323         
29324         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29325     },
29326     
29327     initEvents : function() 
29328     {
29329         if(this.isSubMenu){
29330             return;
29331         }
29332         
29333         this.hidden = true;
29334         
29335         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29336         this.triggerEl.on('click', this.onTriggerPress, this);
29337         
29338         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29339         this.buttonEl.on('click', this.onClick, this);
29340         
29341     },
29342     
29343     list : function()
29344     {
29345         if(this.isSubMenu){
29346             return this.el;
29347         }
29348         
29349         return this.el.select('ul.dropdown-menu', true).first();
29350     },
29351     
29352     onClick : function(e)
29353     {
29354         this.fireEvent("click", this, e);
29355     },
29356     
29357     onTriggerPress  : function(e)
29358     {   
29359         if (this.isVisible()) {
29360             this.hide();
29361         } else {
29362             this.show();
29363         }
29364     },
29365     
29366     isVisible : function(){
29367         return !this.hidden;
29368     },
29369     
29370     show : function()
29371     {
29372         this.fireEvent("beforeshow", this);
29373         
29374         this.hidden = false;
29375         this.el.addClass('open');
29376         
29377         Roo.get(document).on("mouseup", this.onMouseUp, this);
29378         
29379         this.fireEvent("show", this);
29380         
29381         
29382     },
29383     
29384     hide : function()
29385     {
29386         this.fireEvent("beforehide", this);
29387         
29388         this.hidden = true;
29389         this.el.removeClass('open');
29390         
29391         Roo.get(document).un("mouseup", this.onMouseUp);
29392         
29393         this.fireEvent("hide", this);
29394     },
29395     
29396     onMouseUp : function()
29397     {
29398         this.hide();
29399     }
29400     
29401 });
29402
29403  
29404  /*
29405  * - LGPL
29406  *
29407  * menu item
29408  * 
29409  */
29410 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29411
29412 /**
29413  * @class Roo.bootstrap.menu.Item
29414  * @extends Roo.bootstrap.Component
29415  * Bootstrap MenuItem class
29416  * @cfg {Boolean} submenu (true | false) default false
29417  * @cfg {String} html text of the item
29418  * @cfg {String} href the link
29419  * @cfg {Boolean} disable (true | false) default false
29420  * @cfg {Boolean} preventDefault (true | false) default true
29421  * @cfg {String} icon Font awesome icon
29422  * @cfg {String} pos Submenu align to (left | right) default right 
29423  * 
29424  * 
29425  * @constructor
29426  * Create a new Item
29427  * @param {Object} config The config object
29428  */
29429
29430
29431 Roo.bootstrap.menu.Item = function(config){
29432     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29433     this.addEvents({
29434         /**
29435          * @event mouseover
29436          * Fires when the mouse is hovering over this menu
29437          * @param {Roo.bootstrap.menu.Item} this
29438          * @param {Roo.EventObject} e
29439          */
29440         mouseover : true,
29441         /**
29442          * @event mouseout
29443          * Fires when the mouse exits this menu
29444          * @param {Roo.bootstrap.menu.Item} this
29445          * @param {Roo.EventObject} e
29446          */
29447         mouseout : true,
29448         // raw events
29449         /**
29450          * @event click
29451          * The raw click event for the entire grid.
29452          * @param {Roo.EventObject} e
29453          */
29454         click : true
29455     });
29456 };
29457
29458 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29459     
29460     submenu : false,
29461     href : '',
29462     html : '',
29463     preventDefault: true,
29464     disable : false,
29465     icon : false,
29466     pos : 'right',
29467     
29468     getAutoCreate : function()
29469     {
29470         var text = [
29471             {
29472                 tag : 'span',
29473                 cls : 'roo-menu-item-text',
29474                 html : this.html
29475             }
29476         ];
29477         
29478         if(this.icon){
29479             text.unshift({
29480                 tag : 'i',
29481                 cls : 'fa ' + this.icon
29482             })
29483         }
29484         
29485         var cfg = {
29486             tag : 'li',
29487             cn : [
29488                 {
29489                     tag : 'a',
29490                     href : this.href || '#',
29491                     cn : text
29492                 }
29493             ]
29494         };
29495         
29496         if(this.disable){
29497             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29498         }
29499         
29500         if(this.submenu){
29501             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29502             
29503             if(this.pos == 'left'){
29504                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29505             }
29506         }
29507         
29508         return cfg;
29509     },
29510     
29511     initEvents : function() 
29512     {
29513         this.el.on('mouseover', this.onMouseOver, this);
29514         this.el.on('mouseout', this.onMouseOut, this);
29515         
29516         this.el.select('a', true).first().on('click', this.onClick, this);
29517         
29518     },
29519     
29520     onClick : function(e)
29521     {
29522         if(this.preventDefault){
29523             e.preventDefault();
29524         }
29525         
29526         this.fireEvent("click", this, e);
29527     },
29528     
29529     onMouseOver : function(e)
29530     {
29531         if(this.submenu && this.pos == 'left'){
29532             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29533         }
29534         
29535         this.fireEvent("mouseover", this, e);
29536     },
29537     
29538     onMouseOut : function(e)
29539     {
29540         this.fireEvent("mouseout", this, e);
29541     }
29542 });
29543
29544  
29545
29546  /*
29547  * - LGPL
29548  *
29549  * menu separator
29550  * 
29551  */
29552 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29553
29554 /**
29555  * @class Roo.bootstrap.menu.Separator
29556  * @extends Roo.bootstrap.Component
29557  * Bootstrap Separator class
29558  * 
29559  * @constructor
29560  * Create a new Separator
29561  * @param {Object} config The config object
29562  */
29563
29564
29565 Roo.bootstrap.menu.Separator = function(config){
29566     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29567 };
29568
29569 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29570     
29571     getAutoCreate : function(){
29572         var cfg = {
29573             tag : 'li',
29574             cls: 'dropdown-divider divider'
29575         };
29576         
29577         return cfg;
29578     }
29579    
29580 });
29581
29582  
29583
29584  /*
29585  * - LGPL
29586  *
29587  * Tooltip
29588  * 
29589  */
29590
29591 /**
29592  * @class Roo.bootstrap.Tooltip
29593  * Bootstrap Tooltip class
29594  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29595  * to determine which dom element triggers the tooltip.
29596  * 
29597  * It needs to add support for additional attributes like tooltip-position
29598  * 
29599  * @constructor
29600  * Create a new Toolti
29601  * @param {Object} config The config object
29602  */
29603
29604 Roo.bootstrap.Tooltip = function(config){
29605     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29606     
29607     this.alignment = Roo.bootstrap.Tooltip.alignment;
29608     
29609     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29610         this.alignment = config.alignment;
29611     }
29612     
29613 };
29614
29615 Roo.apply(Roo.bootstrap.Tooltip, {
29616     /**
29617      * @function init initialize tooltip monitoring.
29618      * @static
29619      */
29620     currentEl : false,
29621     currentTip : false,
29622     currentRegion : false,
29623     
29624     //  init : delay?
29625     
29626     init : function()
29627     {
29628         Roo.get(document).on('mouseover', this.enter ,this);
29629         Roo.get(document).on('mouseout', this.leave, this);
29630          
29631         
29632         this.currentTip = new Roo.bootstrap.Tooltip();
29633     },
29634     
29635     enter : function(ev)
29636     {
29637         var dom = ev.getTarget();
29638         
29639         //Roo.log(['enter',dom]);
29640         var el = Roo.fly(dom);
29641         if (this.currentEl) {
29642             //Roo.log(dom);
29643             //Roo.log(this.currentEl);
29644             //Roo.log(this.currentEl.contains(dom));
29645             if (this.currentEl == el) {
29646                 return;
29647             }
29648             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29649                 return;
29650             }
29651
29652         }
29653         
29654         if (this.currentTip.el) {
29655             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29656         }    
29657         //Roo.log(ev);
29658         
29659         if(!el || el.dom == document){
29660             return;
29661         }
29662         
29663         var bindEl = el; 
29664         var pel = false;
29665         if (!el.attr('tooltip')) {
29666             pel = el.findParent("[tooltip]");
29667             if (pel) {
29668                 bindEl = Roo.get(pel);
29669             }
29670         }
29671         
29672        
29673         
29674         // you can not look for children, as if el is the body.. then everythign is the child..
29675         if (!pel && !el.attr('tooltip')) { //
29676             if (!el.select("[tooltip]").elements.length) {
29677                 return;
29678             }
29679             // is the mouse over this child...?
29680             bindEl = el.select("[tooltip]").first();
29681             var xy = ev.getXY();
29682             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29683                 //Roo.log("not in region.");
29684                 return;
29685             }
29686             //Roo.log("child element over..");
29687             
29688         }
29689         this.currentEl = el;
29690         this.currentTip.bind(bindEl);
29691         this.currentRegion = Roo.lib.Region.getRegion(dom);
29692         this.currentTip.enter();
29693         
29694     },
29695     leave : function(ev)
29696     {
29697         var dom = ev.getTarget();
29698         //Roo.log(['leave',dom]);
29699         if (!this.currentEl) {
29700             return;
29701         }
29702         
29703         
29704         if (dom != this.currentEl.dom) {
29705             return;
29706         }
29707         var xy = ev.getXY();
29708         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29709             return;
29710         }
29711         // only activate leave if mouse cursor is outside... bounding box..
29712         
29713         
29714         
29715         
29716         if (this.currentTip) {
29717             this.currentTip.leave();
29718         }
29719         //Roo.log('clear currentEl');
29720         this.currentEl = false;
29721         
29722         
29723     },
29724     alignment : {
29725         'left' : ['r-l', [-2,0], 'right'],
29726         'right' : ['l-r', [2,0], 'left'],
29727         'bottom' : ['t-b', [0,2], 'top'],
29728         'top' : [ 'b-t', [0,-2], 'bottom']
29729     }
29730     
29731 });
29732
29733
29734 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29735     
29736     
29737     bindEl : false,
29738     
29739     delay : null, // can be { show : 300 , hide: 500}
29740     
29741     timeout : null,
29742     
29743     hoverState : null, //???
29744     
29745     placement : 'bottom', 
29746     
29747     alignment : false,
29748     
29749     getAutoCreate : function(){
29750     
29751         var cfg = {
29752            cls : 'tooltip',   
29753            role : 'tooltip',
29754            cn : [
29755                 {
29756                     cls : 'tooltip-arrow arrow'
29757                 },
29758                 {
29759                     cls : 'tooltip-inner'
29760                 }
29761            ]
29762         };
29763         
29764         return cfg;
29765     },
29766     bind : function(el)
29767     {
29768         this.bindEl = el;
29769     },
29770     
29771     initEvents : function()
29772     {
29773         this.arrowEl = this.el.select('.arrow', true).first();
29774         this.innerEl = this.el.select('.tooltip-inner', true).first();
29775     },
29776     
29777     enter : function () {
29778        
29779         if (this.timeout != null) {
29780             clearTimeout(this.timeout);
29781         }
29782         
29783         this.hoverState = 'in';
29784          //Roo.log("enter - show");
29785         if (!this.delay || !this.delay.show) {
29786             this.show();
29787             return;
29788         }
29789         var _t = this;
29790         this.timeout = setTimeout(function () {
29791             if (_t.hoverState == 'in') {
29792                 _t.show();
29793             }
29794         }, this.delay.show);
29795     },
29796     leave : function()
29797     {
29798         clearTimeout(this.timeout);
29799     
29800         this.hoverState = 'out';
29801          if (!this.delay || !this.delay.hide) {
29802             this.hide();
29803             return;
29804         }
29805        
29806         var _t = this;
29807         this.timeout = setTimeout(function () {
29808             //Roo.log("leave - timeout");
29809             
29810             if (_t.hoverState == 'out') {
29811                 _t.hide();
29812                 Roo.bootstrap.Tooltip.currentEl = false;
29813             }
29814         }, delay);
29815     },
29816     
29817     show : function (msg)
29818     {
29819         if (!this.el) {
29820             this.render(document.body);
29821         }
29822         // set content.
29823         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29824         
29825         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29826         
29827         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29828         
29829         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29830                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29831         
29832         var placement = typeof this.placement == 'function' ?
29833             this.placement.call(this, this.el, on_el) :
29834             this.placement;
29835             
29836         var autoToken = /\s?auto?\s?/i;
29837         var autoPlace = autoToken.test(placement);
29838         if (autoPlace) {
29839             placement = placement.replace(autoToken, '') || 'top';
29840         }
29841         
29842         //this.el.detach()
29843         //this.el.setXY([0,0]);
29844         this.el.show();
29845         //this.el.dom.style.display='block';
29846         
29847         //this.el.appendTo(on_el);
29848         
29849         var p = this.getPosition();
29850         var box = this.el.getBox();
29851         
29852         if (autoPlace) {
29853             // fixme..
29854         }
29855         
29856         var align = this.alignment[placement];
29857         
29858         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29859         
29860         if(placement == 'top' || placement == 'bottom'){
29861             if(xy[0] < 0){
29862                 placement = 'right';
29863             }
29864             
29865             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29866                 placement = 'left';
29867             }
29868             
29869             var scroll = Roo.select('body', true).first().getScroll();
29870             
29871             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29872                 placement = 'top';
29873             }
29874             
29875             align = this.alignment[placement];
29876             
29877             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29878             
29879         }
29880         
29881         var elems = document.getElementsByTagName('div');
29882         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29883         for (var i = 0; i < elems.length; i++) {
29884           var zindex = Number.parseInt(
29885                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29886                 10
29887           );
29888           if (zindex > highest) {
29889             highest = zindex;
29890           }
29891         }
29892         
29893         
29894         
29895         this.el.dom.style.zIndex = highest;
29896         
29897         this.el.alignTo(this.bindEl, align[0],align[1]);
29898         //var arrow = this.el.select('.arrow',true).first();
29899         //arrow.set(align[2], 
29900         
29901         this.el.addClass(placement);
29902         this.el.addClass("bs-tooltip-"+ placement);
29903         
29904         this.el.addClass('in fade show');
29905         
29906         this.hoverState = null;
29907         
29908         if (this.el.hasClass('fade')) {
29909             // fade it?
29910         }
29911         
29912         
29913         
29914         
29915         
29916     },
29917     hide : function()
29918     {
29919          
29920         if (!this.el) {
29921             return;
29922         }
29923         //this.el.setXY([0,0]);
29924         this.el.removeClass(['show', 'in']);
29925         //this.el.hide();
29926         
29927     }
29928     
29929 });
29930  
29931
29932  /*
29933  * - LGPL
29934  *
29935  * Location Picker
29936  * 
29937  */
29938
29939 /**
29940  * @class Roo.bootstrap.LocationPicker
29941  * @extends Roo.bootstrap.Component
29942  * Bootstrap LocationPicker class
29943  * @cfg {Number} latitude Position when init default 0
29944  * @cfg {Number} longitude Position when init default 0
29945  * @cfg {Number} zoom default 15
29946  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29947  * @cfg {Boolean} mapTypeControl default false
29948  * @cfg {Boolean} disableDoubleClickZoom default false
29949  * @cfg {Boolean} scrollwheel default true
29950  * @cfg {Boolean} streetViewControl default false
29951  * @cfg {Number} radius default 0
29952  * @cfg {String} locationName
29953  * @cfg {Boolean} draggable default true
29954  * @cfg {Boolean} enableAutocomplete default false
29955  * @cfg {Boolean} enableReverseGeocode default true
29956  * @cfg {String} markerTitle
29957  * 
29958  * @constructor
29959  * Create a new LocationPicker
29960  * @param {Object} config The config object
29961  */
29962
29963
29964 Roo.bootstrap.LocationPicker = function(config){
29965     
29966     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29967     
29968     this.addEvents({
29969         /**
29970          * @event initial
29971          * Fires when the picker initialized.
29972          * @param {Roo.bootstrap.LocationPicker} this
29973          * @param {Google Location} location
29974          */
29975         initial : true,
29976         /**
29977          * @event positionchanged
29978          * Fires when the picker position changed.
29979          * @param {Roo.bootstrap.LocationPicker} this
29980          * @param {Google Location} location
29981          */
29982         positionchanged : true,
29983         /**
29984          * @event resize
29985          * Fires when the map resize.
29986          * @param {Roo.bootstrap.LocationPicker} this
29987          */
29988         resize : true,
29989         /**
29990          * @event show
29991          * Fires when the map show.
29992          * @param {Roo.bootstrap.LocationPicker} this
29993          */
29994         show : true,
29995         /**
29996          * @event hide
29997          * Fires when the map hide.
29998          * @param {Roo.bootstrap.LocationPicker} this
29999          */
30000         hide : true,
30001         /**
30002          * @event mapClick
30003          * Fires when click the map.
30004          * @param {Roo.bootstrap.LocationPicker} this
30005          * @param {Map event} e
30006          */
30007         mapClick : true,
30008         /**
30009          * @event mapRightClick
30010          * Fires when right click the map.
30011          * @param {Roo.bootstrap.LocationPicker} this
30012          * @param {Map event} e
30013          */
30014         mapRightClick : true,
30015         /**
30016          * @event markerClick
30017          * Fires when click the marker.
30018          * @param {Roo.bootstrap.LocationPicker} this
30019          * @param {Map event} e
30020          */
30021         markerClick : true,
30022         /**
30023          * @event markerRightClick
30024          * Fires when right click the marker.
30025          * @param {Roo.bootstrap.LocationPicker} this
30026          * @param {Map event} e
30027          */
30028         markerRightClick : true,
30029         /**
30030          * @event OverlayViewDraw
30031          * Fires when OverlayView Draw
30032          * @param {Roo.bootstrap.LocationPicker} this
30033          */
30034         OverlayViewDraw : true,
30035         /**
30036          * @event OverlayViewOnAdd
30037          * Fires when OverlayView Draw
30038          * @param {Roo.bootstrap.LocationPicker} this
30039          */
30040         OverlayViewOnAdd : true,
30041         /**
30042          * @event OverlayViewOnRemove
30043          * Fires when OverlayView Draw
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          */
30046         OverlayViewOnRemove : true,
30047         /**
30048          * @event OverlayViewShow
30049          * Fires when OverlayView Draw
30050          * @param {Roo.bootstrap.LocationPicker} this
30051          * @param {Pixel} cpx
30052          */
30053         OverlayViewShow : true,
30054         /**
30055          * @event OverlayViewHide
30056          * Fires when OverlayView Draw
30057          * @param {Roo.bootstrap.LocationPicker} this
30058          */
30059         OverlayViewHide : true,
30060         /**
30061          * @event loadexception
30062          * Fires when load google lib failed.
30063          * @param {Roo.bootstrap.LocationPicker} this
30064          */
30065         loadexception : true
30066     });
30067         
30068 };
30069
30070 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30071     
30072     gMapContext: false,
30073     
30074     latitude: 0,
30075     longitude: 0,
30076     zoom: 15,
30077     mapTypeId: false,
30078     mapTypeControl: false,
30079     disableDoubleClickZoom: false,
30080     scrollwheel: true,
30081     streetViewControl: false,
30082     radius: 0,
30083     locationName: '',
30084     draggable: true,
30085     enableAutocomplete: false,
30086     enableReverseGeocode: true,
30087     markerTitle: '',
30088     
30089     getAutoCreate: function()
30090     {
30091
30092         var cfg = {
30093             tag: 'div',
30094             cls: 'roo-location-picker'
30095         };
30096         
30097         return cfg
30098     },
30099     
30100     initEvents: function(ct, position)
30101     {       
30102         if(!this.el.getWidth() || this.isApplied()){
30103             return;
30104         }
30105         
30106         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30107         
30108         this.initial();
30109     },
30110     
30111     initial: function()
30112     {
30113         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30114             this.fireEvent('loadexception', this);
30115             return;
30116         }
30117         
30118         if(!this.mapTypeId){
30119             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30120         }
30121         
30122         this.gMapContext = this.GMapContext();
30123         
30124         this.initOverlayView();
30125         
30126         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30127         
30128         var _this = this;
30129                 
30130         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30131             _this.setPosition(_this.gMapContext.marker.position);
30132         });
30133         
30134         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30135             _this.fireEvent('mapClick', this, event);
30136             
30137         });
30138
30139         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30140             _this.fireEvent('mapRightClick', this, event);
30141             
30142         });
30143         
30144         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30145             _this.fireEvent('markerClick', this, event);
30146             
30147         });
30148
30149         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30150             _this.fireEvent('markerRightClick', this, event);
30151             
30152         });
30153         
30154         this.setPosition(this.gMapContext.location);
30155         
30156         this.fireEvent('initial', this, this.gMapContext.location);
30157     },
30158     
30159     initOverlayView: function()
30160     {
30161         var _this = this;
30162         
30163         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30164             
30165             draw: function()
30166             {
30167                 _this.fireEvent('OverlayViewDraw', _this);
30168             },
30169             
30170             onAdd: function()
30171             {
30172                 _this.fireEvent('OverlayViewOnAdd', _this);
30173             },
30174             
30175             onRemove: function()
30176             {
30177                 _this.fireEvent('OverlayViewOnRemove', _this);
30178             },
30179             
30180             show: function(cpx)
30181             {
30182                 _this.fireEvent('OverlayViewShow', _this, cpx);
30183             },
30184             
30185             hide: function()
30186             {
30187                 _this.fireEvent('OverlayViewHide', _this);
30188             }
30189             
30190         });
30191     },
30192     
30193     fromLatLngToContainerPixel: function(event)
30194     {
30195         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30196     },
30197     
30198     isApplied: function() 
30199     {
30200         return this.getGmapContext() == false ? false : true;
30201     },
30202     
30203     getGmapContext: function() 
30204     {
30205         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30206     },
30207     
30208     GMapContext: function() 
30209     {
30210         var position = new google.maps.LatLng(this.latitude, this.longitude);
30211         
30212         var _map = new google.maps.Map(this.el.dom, {
30213             center: position,
30214             zoom: this.zoom,
30215             mapTypeId: this.mapTypeId,
30216             mapTypeControl: this.mapTypeControl,
30217             disableDoubleClickZoom: this.disableDoubleClickZoom,
30218             scrollwheel: this.scrollwheel,
30219             streetViewControl: this.streetViewControl,
30220             locationName: this.locationName,
30221             draggable: this.draggable,
30222             enableAutocomplete: this.enableAutocomplete,
30223             enableReverseGeocode: this.enableReverseGeocode
30224         });
30225         
30226         var _marker = new google.maps.Marker({
30227             position: position,
30228             map: _map,
30229             title: this.markerTitle,
30230             draggable: this.draggable
30231         });
30232         
30233         return {
30234             map: _map,
30235             marker: _marker,
30236             circle: null,
30237             location: position,
30238             radius: this.radius,
30239             locationName: this.locationName,
30240             addressComponents: {
30241                 formatted_address: null,
30242                 addressLine1: null,
30243                 addressLine2: null,
30244                 streetName: null,
30245                 streetNumber: null,
30246                 city: null,
30247                 district: null,
30248                 state: null,
30249                 stateOrProvince: null
30250             },
30251             settings: this,
30252             domContainer: this.el.dom,
30253             geodecoder: new google.maps.Geocoder()
30254         };
30255     },
30256     
30257     drawCircle: function(center, radius, options) 
30258     {
30259         if (this.gMapContext.circle != null) {
30260             this.gMapContext.circle.setMap(null);
30261         }
30262         if (radius > 0) {
30263             radius *= 1;
30264             options = Roo.apply({}, options, {
30265                 strokeColor: "#0000FF",
30266                 strokeOpacity: .35,
30267                 strokeWeight: 2,
30268                 fillColor: "#0000FF",
30269                 fillOpacity: .2
30270             });
30271             
30272             options.map = this.gMapContext.map;
30273             options.radius = radius;
30274             options.center = center;
30275             this.gMapContext.circle = new google.maps.Circle(options);
30276             return this.gMapContext.circle;
30277         }
30278         
30279         return null;
30280     },
30281     
30282     setPosition: function(location) 
30283     {
30284         this.gMapContext.location = location;
30285         this.gMapContext.marker.setPosition(location);
30286         this.gMapContext.map.panTo(location);
30287         this.drawCircle(location, this.gMapContext.radius, {});
30288         
30289         var _this = this;
30290         
30291         if (this.gMapContext.settings.enableReverseGeocode) {
30292             this.gMapContext.geodecoder.geocode({
30293                 latLng: this.gMapContext.location
30294             }, function(results, status) {
30295                 
30296                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30297                     _this.gMapContext.locationName = results[0].formatted_address;
30298                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30299                     
30300                     _this.fireEvent('positionchanged', this, location);
30301                 }
30302             });
30303             
30304             return;
30305         }
30306         
30307         this.fireEvent('positionchanged', this, location);
30308     },
30309     
30310     resize: function()
30311     {
30312         google.maps.event.trigger(this.gMapContext.map, "resize");
30313         
30314         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30315         
30316         this.fireEvent('resize', this);
30317     },
30318     
30319     setPositionByLatLng: function(latitude, longitude)
30320     {
30321         this.setPosition(new google.maps.LatLng(latitude, longitude));
30322     },
30323     
30324     getCurrentPosition: function() 
30325     {
30326         return {
30327             latitude: this.gMapContext.location.lat(),
30328             longitude: this.gMapContext.location.lng()
30329         };
30330     },
30331     
30332     getAddressName: function() 
30333     {
30334         return this.gMapContext.locationName;
30335     },
30336     
30337     getAddressComponents: function() 
30338     {
30339         return this.gMapContext.addressComponents;
30340     },
30341     
30342     address_component_from_google_geocode: function(address_components) 
30343     {
30344         var result = {};
30345         
30346         for (var i = 0; i < address_components.length; i++) {
30347             var component = address_components[i];
30348             if (component.types.indexOf("postal_code") >= 0) {
30349                 result.postalCode = component.short_name;
30350             } else if (component.types.indexOf("street_number") >= 0) {
30351                 result.streetNumber = component.short_name;
30352             } else if (component.types.indexOf("route") >= 0) {
30353                 result.streetName = component.short_name;
30354             } else if (component.types.indexOf("neighborhood") >= 0) {
30355                 result.city = component.short_name;
30356             } else if (component.types.indexOf("locality") >= 0) {
30357                 result.city = component.short_name;
30358             } else if (component.types.indexOf("sublocality") >= 0) {
30359                 result.district = component.short_name;
30360             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30361                 result.stateOrProvince = component.short_name;
30362             } else if (component.types.indexOf("country") >= 0) {
30363                 result.country = component.short_name;
30364             }
30365         }
30366         
30367         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30368         result.addressLine2 = "";
30369         return result;
30370     },
30371     
30372     setZoomLevel: function(zoom)
30373     {
30374         this.gMapContext.map.setZoom(zoom);
30375     },
30376     
30377     show: function()
30378     {
30379         if(!this.el){
30380             return;
30381         }
30382         
30383         this.el.show();
30384         
30385         this.resize();
30386         
30387         this.fireEvent('show', this);
30388     },
30389     
30390     hide: function()
30391     {
30392         if(!this.el){
30393             return;
30394         }
30395         
30396         this.el.hide();
30397         
30398         this.fireEvent('hide', this);
30399     }
30400     
30401 });
30402
30403 Roo.apply(Roo.bootstrap.LocationPicker, {
30404     
30405     OverlayView : function(map, options)
30406     {
30407         options = options || {};
30408         
30409         this.setMap(map);
30410     }
30411     
30412     
30413 });/**
30414  * @class Roo.bootstrap.Alert
30415  * @extends Roo.bootstrap.Component
30416  * Bootstrap Alert class - shows an alert area box
30417  * eg
30418  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30419   Enter a valid email address
30420 </div>
30421  * @licence LGPL
30422  * @cfg {String} title The title of alert
30423  * @cfg {String} html The content of alert
30424  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30425  * @cfg {String} fa font-awesomeicon
30426  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30427  * @cfg {Boolean} close true to show a x closer
30428  * 
30429  * 
30430  * @constructor
30431  * Create a new alert
30432  * @param {Object} config The config object
30433  */
30434
30435
30436 Roo.bootstrap.Alert = function(config){
30437     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30438     
30439 };
30440
30441 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30442     
30443     title: '',
30444     html: '',
30445     weight: false,
30446     fa: false,
30447     faicon: false, // BC
30448     close : false,
30449     
30450     
30451     getAutoCreate : function()
30452     {
30453         
30454         var cfg = {
30455             tag : 'div',
30456             cls : 'alert',
30457             cn : [
30458                 {
30459                     tag: 'button',
30460                     type :  "button",
30461                     cls: "close",
30462                     html : '×',
30463                     style : this.close ? '' : 'display:none'
30464                 },
30465                 {
30466                     tag : 'i',
30467                     cls : 'roo-alert-icon'
30468                     
30469                 },
30470                 {
30471                     tag : 'b',
30472                     cls : 'roo-alert-title',
30473                     html : this.title
30474                 },
30475                 {
30476                     tag : 'span',
30477                     cls : 'roo-alert-text',
30478                     html : this.html
30479                 }
30480             ]
30481         };
30482         
30483         if(this.faicon){
30484             cfg.cn[0].cls += ' fa ' + this.faicon;
30485         }
30486         if(this.fa){
30487             cfg.cn[0].cls += ' fa ' + this.fa;
30488         }
30489         
30490         if(this.weight){
30491             cfg.cls += ' alert-' + this.weight;
30492         }
30493         
30494         return cfg;
30495     },
30496     
30497     initEvents: function() 
30498     {
30499         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30500         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30501         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30502         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30503         if (this.seconds > 0) {
30504             this.hide.defer(this.seconds, this);
30505         }
30506     },
30507     /**
30508      * Set the Title Message HTML
30509      * @param {String} html
30510      */
30511     setTitle : function(str)
30512     {
30513         this.titleEl.dom.innerHTML = str;
30514     },
30515      
30516      /**
30517      * Set the Body Message HTML
30518      * @param {String} html
30519      */
30520     setHtml : function(str)
30521     {
30522         this.htmlEl.dom.innerHTML = str;
30523     },
30524     /**
30525      * Set the Weight of the alert
30526      * @param {String} (success|info|warning|danger) weight
30527      */
30528     
30529     setWeight : function(weight)
30530     {
30531         if(this.weight){
30532             this.el.removeClass('alert-' + this.weight);
30533         }
30534         
30535         this.weight = weight;
30536         
30537         this.el.addClass('alert-' + this.weight);
30538     },
30539       /**
30540      * Set the Icon of the alert
30541      * @param {String} see fontawsome names (name without the 'fa-' bit)
30542      */
30543     setIcon : function(icon)
30544     {
30545         if(this.faicon){
30546             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30547         }
30548         
30549         this.faicon = icon;
30550         
30551         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30552     },
30553     /**
30554      * Hide the Alert
30555      */
30556     hide: function() 
30557     {
30558         this.el.hide();   
30559     },
30560     /**
30561      * Show the Alert
30562      */
30563     show: function() 
30564     {  
30565         this.el.show();   
30566     }
30567     
30568 });
30569
30570  
30571 /*
30572 * Licence: LGPL
30573 */
30574
30575 /**
30576  * @class Roo.bootstrap.UploadCropbox
30577  * @extends Roo.bootstrap.Component
30578  * Bootstrap UploadCropbox class
30579  * @cfg {String} emptyText show when image has been loaded
30580  * @cfg {String} rotateNotify show when image too small to rotate
30581  * @cfg {Number} errorTimeout default 3000
30582  * @cfg {Number} minWidth default 300
30583  * @cfg {Number} minHeight default 300
30584  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30585  * @cfg {Boolean} isDocument (true|false) default false
30586  * @cfg {String} url action url
30587  * @cfg {String} paramName default 'imageUpload'
30588  * @cfg {String} method default POST
30589  * @cfg {Boolean} loadMask (true|false) default true
30590  * @cfg {Boolean} loadingText default 'Loading...'
30591  * 
30592  * @constructor
30593  * Create a new UploadCropbox
30594  * @param {Object} config The config object
30595  */
30596
30597 Roo.bootstrap.UploadCropbox = function(config){
30598     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30599     
30600     this.addEvents({
30601         /**
30602          * @event beforeselectfile
30603          * Fire before select file
30604          * @param {Roo.bootstrap.UploadCropbox} this
30605          */
30606         "beforeselectfile" : true,
30607         /**
30608          * @event initial
30609          * Fire after initEvent
30610          * @param {Roo.bootstrap.UploadCropbox} this
30611          */
30612         "initial" : true,
30613         /**
30614          * @event crop
30615          * Fire after initEvent
30616          * @param {Roo.bootstrap.UploadCropbox} this
30617          * @param {String} data
30618          */
30619         "crop" : true,
30620         /**
30621          * @event prepare
30622          * Fire when preparing the file data
30623          * @param {Roo.bootstrap.UploadCropbox} this
30624          * @param {Object} file
30625          */
30626         "prepare" : true,
30627         /**
30628          * @event exception
30629          * Fire when get exception
30630          * @param {Roo.bootstrap.UploadCropbox} this
30631          * @param {XMLHttpRequest} xhr
30632          */
30633         "exception" : true,
30634         /**
30635          * @event beforeloadcanvas
30636          * Fire before load the canvas
30637          * @param {Roo.bootstrap.UploadCropbox} this
30638          * @param {String} src
30639          */
30640         "beforeloadcanvas" : true,
30641         /**
30642          * @event trash
30643          * Fire when trash image
30644          * @param {Roo.bootstrap.UploadCropbox} this
30645          */
30646         "trash" : true,
30647         /**
30648          * @event download
30649          * Fire when download the image
30650          * @param {Roo.bootstrap.UploadCropbox} this
30651          */
30652         "download" : true,
30653         /**
30654          * @event footerbuttonclick
30655          * Fire when footerbuttonclick
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          * @param {String} type
30658          */
30659         "footerbuttonclick" : true,
30660         /**
30661          * @event resize
30662          * Fire when resize
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          */
30665         "resize" : true,
30666         /**
30667          * @event rotate
30668          * Fire when rotate the image
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          * @param {String} pos
30671          */
30672         "rotate" : true,
30673         /**
30674          * @event inspect
30675          * Fire when inspect the file
30676          * @param {Roo.bootstrap.UploadCropbox} this
30677          * @param {Object} file
30678          */
30679         "inspect" : true,
30680         /**
30681          * @event upload
30682          * Fire when xhr upload the file
30683          * @param {Roo.bootstrap.UploadCropbox} this
30684          * @param {Object} data
30685          */
30686         "upload" : true,
30687         /**
30688          * @event arrange
30689          * Fire when arrange the file data
30690          * @param {Roo.bootstrap.UploadCropbox} this
30691          * @param {Object} formData
30692          */
30693         "arrange" : true
30694     });
30695     
30696     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30697 };
30698
30699 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30700     
30701     emptyText : 'Click to upload image',
30702     rotateNotify : 'Image is too small to rotate',
30703     errorTimeout : 3000,
30704     scale : 0,
30705     baseScale : 1,
30706     rotate : 0,
30707     dragable : false,
30708     pinching : false,
30709     mouseX : 0,
30710     mouseY : 0,
30711     cropData : false,
30712     minWidth : 300,
30713     minHeight : 300,
30714     file : false,
30715     exif : {},
30716     baseRotate : 1,
30717     cropType : 'image/jpeg',
30718     buttons : false,
30719     canvasLoaded : false,
30720     isDocument : false,
30721     method : 'POST',
30722     paramName : 'imageUpload',
30723     loadMask : true,
30724     loadingText : 'Loading...',
30725     maskEl : false,
30726     
30727     getAutoCreate : function()
30728     {
30729         var cfg = {
30730             tag : 'div',
30731             cls : 'roo-upload-cropbox',
30732             cn : [
30733                 {
30734                     tag : 'input',
30735                     cls : 'roo-upload-cropbox-selector',
30736                     type : 'file'
30737                 },
30738                 {
30739                     tag : 'div',
30740                     cls : 'roo-upload-cropbox-body',
30741                     style : 'cursor:pointer',
30742                     cn : [
30743                         {
30744                             tag : 'div',
30745                             cls : 'roo-upload-cropbox-preview'
30746                         },
30747                         {
30748                             tag : 'div',
30749                             cls : 'roo-upload-cropbox-thumb'
30750                         },
30751                         {
30752                             tag : 'div',
30753                             cls : 'roo-upload-cropbox-empty-notify',
30754                             html : this.emptyText
30755                         },
30756                         {
30757                             tag : 'div',
30758                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30759                             html : this.rotateNotify
30760                         }
30761                     ]
30762                 },
30763                 {
30764                     tag : 'div',
30765                     cls : 'roo-upload-cropbox-footer',
30766                     cn : {
30767                         tag : 'div',
30768                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30769                         cn : []
30770                     }
30771                 }
30772             ]
30773         };
30774         
30775         return cfg;
30776     },
30777     
30778     onRender : function(ct, position)
30779     {
30780         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30781         
30782         if (this.buttons.length) {
30783             
30784             Roo.each(this.buttons, function(bb) {
30785                 
30786                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30787                 
30788                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30789                 
30790             }, this);
30791         }
30792         
30793         if(this.loadMask){
30794             this.maskEl = this.el;
30795         }
30796     },
30797     
30798     initEvents : function()
30799     {
30800         this.urlAPI = (window.createObjectURL && window) || 
30801                                 (window.URL && URL.revokeObjectURL && URL) || 
30802                                 (window.webkitURL && webkitURL);
30803                         
30804         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30805         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30806         
30807         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30808         this.selectorEl.hide();
30809         
30810         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30811         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30812         
30813         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30814         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30815         this.thumbEl.hide();
30816         
30817         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30818         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819         
30820         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30821         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30822         this.errorEl.hide();
30823         
30824         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30825         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30826         this.footerEl.hide();
30827         
30828         this.setThumbBoxSize();
30829         
30830         this.bind();
30831         
30832         this.resize();
30833         
30834         this.fireEvent('initial', this);
30835     },
30836
30837     bind : function()
30838     {
30839         var _this = this;
30840         
30841         window.addEventListener("resize", function() { _this.resize(); } );
30842         
30843         this.bodyEl.on('click', this.beforeSelectFile, this);
30844         
30845         if(Roo.isTouch){
30846             this.bodyEl.on('touchstart', this.onTouchStart, this);
30847             this.bodyEl.on('touchmove', this.onTouchMove, this);
30848             this.bodyEl.on('touchend', this.onTouchEnd, this);
30849         }
30850         
30851         if(!Roo.isTouch){
30852             this.bodyEl.on('mousedown', this.onMouseDown, this);
30853             this.bodyEl.on('mousemove', this.onMouseMove, this);
30854             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30855             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30856             Roo.get(document).on('mouseup', this.onMouseUp, this);
30857         }
30858         
30859         this.selectorEl.on('change', this.onFileSelected, this);
30860     },
30861     
30862     reset : function()
30863     {    
30864         this.scale = 0;
30865         this.baseScale = 1;
30866         this.rotate = 0;
30867         this.baseRotate = 1;
30868         this.dragable = false;
30869         this.pinching = false;
30870         this.mouseX = 0;
30871         this.mouseY = 0;
30872         this.cropData = false;
30873         this.notifyEl.dom.innerHTML = this.emptyText;
30874         
30875         this.selectorEl.dom.value = '';
30876         
30877     },
30878     
30879     resize : function()
30880     {
30881         if(this.fireEvent('resize', this) != false){
30882             this.setThumbBoxPosition();
30883             this.setCanvasPosition();
30884         }
30885     },
30886     
30887     onFooterButtonClick : function(e, el, o, type)
30888     {
30889         switch (type) {
30890             case 'rotate-left' :
30891                 this.onRotateLeft(e);
30892                 break;
30893             case 'rotate-right' :
30894                 this.onRotateRight(e);
30895                 break;
30896             case 'picture' :
30897                 this.beforeSelectFile(e);
30898                 break;
30899             case 'trash' :
30900                 this.trash(e);
30901                 break;
30902             case 'crop' :
30903                 this.crop(e);
30904                 break;
30905             case 'download' :
30906                 this.download(e);
30907                 break;
30908             default :
30909                 break;
30910         }
30911         
30912         this.fireEvent('footerbuttonclick', this, type);
30913     },
30914     
30915     beforeSelectFile : function(e)
30916     {
30917         e.preventDefault();
30918         
30919         if(this.fireEvent('beforeselectfile', this) != false){
30920             this.selectorEl.dom.click();
30921         }
30922     },
30923     
30924     onFileSelected : function(e)
30925     {
30926         e.preventDefault();
30927         
30928         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30929             return;
30930         }
30931         
30932         var file = this.selectorEl.dom.files[0];
30933         
30934         if(this.fireEvent('inspect', this, file) != false){
30935             this.prepare(file);
30936         }
30937         
30938     },
30939     
30940     trash : function(e)
30941     {
30942         this.fireEvent('trash', this);
30943     },
30944     
30945     download : function(e)
30946     {
30947         this.fireEvent('download', this);
30948     },
30949     
30950     loadCanvas : function(src)
30951     {   
30952         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30953             
30954             this.reset();
30955             
30956             this.imageEl = document.createElement('img');
30957             
30958             var _this = this;
30959             
30960             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30961             
30962             this.imageEl.src = src;
30963         }
30964     },
30965     
30966     onLoadCanvas : function()
30967     {   
30968         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30969         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30970         
30971         this.bodyEl.un('click', this.beforeSelectFile, this);
30972         
30973         this.notifyEl.hide();
30974         this.thumbEl.show();
30975         this.footerEl.show();
30976         
30977         this.baseRotateLevel();
30978         
30979         if(this.isDocument){
30980             this.setThumbBoxSize();
30981         }
30982         
30983         this.setThumbBoxPosition();
30984         
30985         this.baseScaleLevel();
30986         
30987         this.draw();
30988         
30989         this.resize();
30990         
30991         this.canvasLoaded = true;
30992         
30993         if(this.loadMask){
30994             this.maskEl.unmask();
30995         }
30996         
30997     },
30998     
30999     setCanvasPosition : function()
31000     {   
31001         if(!this.canvasEl){
31002             return;
31003         }
31004         
31005         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31006         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31007         
31008         this.previewEl.setLeft(pw);
31009         this.previewEl.setTop(ph);
31010         
31011     },
31012     
31013     onMouseDown : function(e)
31014     {   
31015         e.stopEvent();
31016         
31017         this.dragable = true;
31018         this.pinching = false;
31019         
31020         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31021             this.dragable = false;
31022             return;
31023         }
31024         
31025         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31026         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31027         
31028     },
31029     
31030     onMouseMove : function(e)
31031     {   
31032         e.stopEvent();
31033         
31034         if(!this.canvasLoaded){
31035             return;
31036         }
31037         
31038         if (!this.dragable){
31039             return;
31040         }
31041         
31042         var minX = Math.ceil(this.thumbEl.getLeft(true));
31043         var minY = Math.ceil(this.thumbEl.getTop(true));
31044         
31045         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31046         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31047         
31048         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31049         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31050         
31051         x = x - this.mouseX;
31052         y = y - this.mouseY;
31053         
31054         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31055         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31056         
31057         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31058         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31059         
31060         this.previewEl.setLeft(bgX);
31061         this.previewEl.setTop(bgY);
31062         
31063         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31064         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31065     },
31066     
31067     onMouseUp : function(e)
31068     {   
31069         e.stopEvent();
31070         
31071         this.dragable = false;
31072     },
31073     
31074     onMouseWheel : function(e)
31075     {   
31076         e.stopEvent();
31077         
31078         this.startScale = this.scale;
31079         
31080         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31081         
31082         if(!this.zoomable()){
31083             this.scale = this.startScale;
31084             return;
31085         }
31086         
31087         this.draw();
31088         
31089         return;
31090     },
31091     
31092     zoomable : function()
31093     {
31094         var minScale = this.thumbEl.getWidth() / this.minWidth;
31095         
31096         if(this.minWidth < this.minHeight){
31097             minScale = this.thumbEl.getHeight() / this.minHeight;
31098         }
31099         
31100         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31101         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31102         
31103         if(
31104                 this.isDocument &&
31105                 (this.rotate == 0 || this.rotate == 180) && 
31106                 (
31107                     width > this.imageEl.OriginWidth || 
31108                     height > this.imageEl.OriginHeight ||
31109                     (width < this.minWidth && height < this.minHeight)
31110                 )
31111         ){
31112             return false;
31113         }
31114         
31115         if(
31116                 this.isDocument &&
31117                 (this.rotate == 90 || this.rotate == 270) && 
31118                 (
31119                     width > this.imageEl.OriginWidth || 
31120                     height > this.imageEl.OriginHeight ||
31121                     (width < this.minHeight && height < this.minWidth)
31122                 )
31123         ){
31124             return false;
31125         }
31126         
31127         if(
31128                 !this.isDocument &&
31129                 (this.rotate == 0 || this.rotate == 180) && 
31130                 (
31131                     width < this.minWidth || 
31132                     width > this.imageEl.OriginWidth || 
31133                     height < this.minHeight || 
31134                     height > this.imageEl.OriginHeight
31135                 )
31136         ){
31137             return false;
31138         }
31139         
31140         if(
31141                 !this.isDocument &&
31142                 (this.rotate == 90 || this.rotate == 270) && 
31143                 (
31144                     width < this.minHeight || 
31145                     width > this.imageEl.OriginWidth || 
31146                     height < this.minWidth || 
31147                     height > this.imageEl.OriginHeight
31148                 )
31149         ){
31150             return false;
31151         }
31152         
31153         return true;
31154         
31155     },
31156     
31157     onRotateLeft : function(e)
31158     {   
31159         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31160             
31161             var minScale = this.thumbEl.getWidth() / this.minWidth;
31162             
31163             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31164             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31165             
31166             this.startScale = this.scale;
31167             
31168             while (this.getScaleLevel() < minScale){
31169             
31170                 this.scale = this.scale + 1;
31171                 
31172                 if(!this.zoomable()){
31173                     break;
31174                 }
31175                 
31176                 if(
31177                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31178                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31179                 ){
31180                     continue;
31181                 }
31182                 
31183                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31184
31185                 this.draw();
31186                 
31187                 return;
31188             }
31189             
31190             this.scale = this.startScale;
31191             
31192             this.onRotateFail();
31193             
31194             return false;
31195         }
31196         
31197         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31198
31199         if(this.isDocument){
31200             this.setThumbBoxSize();
31201             this.setThumbBoxPosition();
31202             this.setCanvasPosition();
31203         }
31204         
31205         this.draw();
31206         
31207         this.fireEvent('rotate', this, 'left');
31208         
31209     },
31210     
31211     onRotateRight : function(e)
31212     {
31213         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31214             
31215             var minScale = this.thumbEl.getWidth() / this.minWidth;
31216         
31217             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31218             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31219             
31220             this.startScale = this.scale;
31221             
31222             while (this.getScaleLevel() < minScale){
31223             
31224                 this.scale = this.scale + 1;
31225                 
31226                 if(!this.zoomable()){
31227                     break;
31228                 }
31229                 
31230                 if(
31231                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31232                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31233                 ){
31234                     continue;
31235                 }
31236                 
31237                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31238
31239                 this.draw();
31240                 
31241                 return;
31242             }
31243             
31244             this.scale = this.startScale;
31245             
31246             this.onRotateFail();
31247             
31248             return false;
31249         }
31250         
31251         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31252
31253         if(this.isDocument){
31254             this.setThumbBoxSize();
31255             this.setThumbBoxPosition();
31256             this.setCanvasPosition();
31257         }
31258         
31259         this.draw();
31260         
31261         this.fireEvent('rotate', this, 'right');
31262     },
31263     
31264     onRotateFail : function()
31265     {
31266         this.errorEl.show(true);
31267         
31268         var _this = this;
31269         
31270         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31271     },
31272     
31273     draw : function()
31274     {
31275         this.previewEl.dom.innerHTML = '';
31276         
31277         var canvasEl = document.createElement("canvas");
31278         
31279         var contextEl = canvasEl.getContext("2d");
31280         
31281         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31282         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31283         var center = this.imageEl.OriginWidth / 2;
31284         
31285         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31286             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31287             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31288             center = this.imageEl.OriginHeight / 2;
31289         }
31290         
31291         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31292         
31293         contextEl.translate(center, center);
31294         contextEl.rotate(this.rotate * Math.PI / 180);
31295
31296         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31297         
31298         this.canvasEl = document.createElement("canvas");
31299         
31300         this.contextEl = this.canvasEl.getContext("2d");
31301         
31302         switch (this.rotate) {
31303             case 0 :
31304                 
31305                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31306                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31307                 
31308                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31309                 
31310                 break;
31311             case 90 : 
31312                 
31313                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31314                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31315                 
31316                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31317                     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);
31318                     break;
31319                 }
31320                 
31321                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31322                 
31323                 break;
31324             case 180 :
31325                 
31326                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31327                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31328                 
31329                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31330                     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);
31331                     break;
31332                 }
31333                 
31334                 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);
31335                 
31336                 break;
31337             case 270 :
31338                 
31339                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31340                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31341         
31342                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31343                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31344                     break;
31345                 }
31346                 
31347                 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);
31348                 
31349                 break;
31350             default : 
31351                 break;
31352         }
31353         
31354         this.previewEl.appendChild(this.canvasEl);
31355         
31356         this.setCanvasPosition();
31357     },
31358     
31359     crop : function()
31360     {
31361         if(!this.canvasLoaded){
31362             return;
31363         }
31364         
31365         var imageCanvas = document.createElement("canvas");
31366         
31367         var imageContext = imageCanvas.getContext("2d");
31368         
31369         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31370         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31371         
31372         var center = imageCanvas.width / 2;
31373         
31374         imageContext.translate(center, center);
31375         
31376         imageContext.rotate(this.rotate * Math.PI / 180);
31377         
31378         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31379         
31380         var canvas = document.createElement("canvas");
31381         
31382         var context = canvas.getContext("2d");
31383                 
31384         canvas.width = this.minWidth;
31385         canvas.height = this.minHeight;
31386
31387         switch (this.rotate) {
31388             case 0 :
31389                 
31390                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31391                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31392                 
31393                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31394                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31395                 
31396                 var targetWidth = this.minWidth - 2 * x;
31397                 var targetHeight = this.minHeight - 2 * y;
31398                 
31399                 var scale = 1;
31400                 
31401                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31402                     scale = targetWidth / width;
31403                 }
31404                 
31405                 if(x > 0 && y == 0){
31406                     scale = targetHeight / height;
31407                 }
31408                 
31409                 if(x > 0 && y > 0){
31410                     scale = targetWidth / width;
31411                     
31412                     if(width < height){
31413                         scale = targetHeight / height;
31414                     }
31415                 }
31416                 
31417                 context.scale(scale, scale);
31418                 
31419                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31420                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31421
31422                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31423                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31424
31425                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31426                 
31427                 break;
31428             case 90 : 
31429                 
31430                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31431                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31432                 
31433                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31434                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31435                 
31436                 var targetWidth = this.minWidth - 2 * x;
31437                 var targetHeight = this.minHeight - 2 * y;
31438                 
31439                 var scale = 1;
31440                 
31441                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31442                     scale = targetWidth / width;
31443                 }
31444                 
31445                 if(x > 0 && y == 0){
31446                     scale = targetHeight / height;
31447                 }
31448                 
31449                 if(x > 0 && y > 0){
31450                     scale = targetWidth / width;
31451                     
31452                     if(width < height){
31453                         scale = targetHeight / height;
31454                     }
31455                 }
31456                 
31457                 context.scale(scale, scale);
31458                 
31459                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31460                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31461
31462                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31463                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31464                 
31465                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31466                 
31467                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31468                 
31469                 break;
31470             case 180 :
31471                 
31472                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31473                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31474                 
31475                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31476                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31477                 
31478                 var targetWidth = this.minWidth - 2 * x;
31479                 var targetHeight = this.minHeight - 2 * y;
31480                 
31481                 var scale = 1;
31482                 
31483                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31484                     scale = targetWidth / width;
31485                 }
31486                 
31487                 if(x > 0 && y == 0){
31488                     scale = targetHeight / height;
31489                 }
31490                 
31491                 if(x > 0 && y > 0){
31492                     scale = targetWidth / width;
31493                     
31494                     if(width < height){
31495                         scale = targetHeight / height;
31496                     }
31497                 }
31498                 
31499                 context.scale(scale, scale);
31500                 
31501                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31502                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31503
31504                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31505                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31506
31507                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31508                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31509                 
31510                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31511                 
31512                 break;
31513             case 270 :
31514                 
31515                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31516                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31517                 
31518                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31519                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31520                 
31521                 var targetWidth = this.minWidth - 2 * x;
31522                 var targetHeight = this.minHeight - 2 * y;
31523                 
31524                 var scale = 1;
31525                 
31526                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31527                     scale = targetWidth / width;
31528                 }
31529                 
31530                 if(x > 0 && y == 0){
31531                     scale = targetHeight / height;
31532                 }
31533                 
31534                 if(x > 0 && y > 0){
31535                     scale = targetWidth / width;
31536                     
31537                     if(width < height){
31538                         scale = targetHeight / height;
31539                     }
31540                 }
31541                 
31542                 context.scale(scale, scale);
31543                 
31544                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31545                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31546
31547                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31548                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31549                 
31550                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31551                 
31552                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31553                 
31554                 break;
31555             default : 
31556                 break;
31557         }
31558         
31559         this.cropData = canvas.toDataURL(this.cropType);
31560         
31561         if(this.fireEvent('crop', this, this.cropData) !== false){
31562             this.process(this.file, this.cropData);
31563         }
31564         
31565         return;
31566         
31567     },
31568     
31569     setThumbBoxSize : function()
31570     {
31571         var width, height;
31572         
31573         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31574             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31575             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31576             
31577             this.minWidth = width;
31578             this.minHeight = height;
31579             
31580             if(this.rotate == 90 || this.rotate == 270){
31581                 this.minWidth = height;
31582                 this.minHeight = width;
31583             }
31584         }
31585         
31586         height = 300;
31587         width = Math.ceil(this.minWidth * height / this.minHeight);
31588         
31589         if(this.minWidth > this.minHeight){
31590             width = 300;
31591             height = Math.ceil(this.minHeight * width / this.minWidth);
31592         }
31593         
31594         this.thumbEl.setStyle({
31595             width : width + 'px',
31596             height : height + 'px'
31597         });
31598
31599         return;
31600             
31601     },
31602     
31603     setThumbBoxPosition : function()
31604     {
31605         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31606         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31607         
31608         this.thumbEl.setLeft(x);
31609         this.thumbEl.setTop(y);
31610         
31611     },
31612     
31613     baseRotateLevel : function()
31614     {
31615         this.baseRotate = 1;
31616         
31617         if(
31618                 typeof(this.exif) != 'undefined' &&
31619                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31620                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31621         ){
31622             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31623         }
31624         
31625         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31626         
31627     },
31628     
31629     baseScaleLevel : function()
31630     {
31631         var width, height;
31632         
31633         if(this.isDocument){
31634             
31635             if(this.baseRotate == 6 || this.baseRotate == 8){
31636             
31637                 height = this.thumbEl.getHeight();
31638                 this.baseScale = height / this.imageEl.OriginWidth;
31639
31640                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31641                     width = this.thumbEl.getWidth();
31642                     this.baseScale = width / this.imageEl.OriginHeight;
31643                 }
31644
31645                 return;
31646             }
31647
31648             height = this.thumbEl.getHeight();
31649             this.baseScale = height / this.imageEl.OriginHeight;
31650
31651             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31652                 width = this.thumbEl.getWidth();
31653                 this.baseScale = width / this.imageEl.OriginWidth;
31654             }
31655
31656             return;
31657         }
31658         
31659         if(this.baseRotate == 6 || this.baseRotate == 8){
31660             
31661             width = this.thumbEl.getHeight();
31662             this.baseScale = width / this.imageEl.OriginHeight;
31663             
31664             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31665                 height = this.thumbEl.getWidth();
31666                 this.baseScale = height / this.imageEl.OriginHeight;
31667             }
31668             
31669             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31670                 height = this.thumbEl.getWidth();
31671                 this.baseScale = height / this.imageEl.OriginHeight;
31672                 
31673                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31674                     width = this.thumbEl.getHeight();
31675                     this.baseScale = width / this.imageEl.OriginWidth;
31676                 }
31677             }
31678             
31679             return;
31680         }
31681         
31682         width = this.thumbEl.getWidth();
31683         this.baseScale = width / this.imageEl.OriginWidth;
31684         
31685         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31686             height = this.thumbEl.getHeight();
31687             this.baseScale = height / this.imageEl.OriginHeight;
31688         }
31689         
31690         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31691             
31692             height = this.thumbEl.getHeight();
31693             this.baseScale = height / this.imageEl.OriginHeight;
31694             
31695             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31696                 width = this.thumbEl.getWidth();
31697                 this.baseScale = width / this.imageEl.OriginWidth;
31698             }
31699             
31700         }
31701         
31702         return;
31703     },
31704     
31705     getScaleLevel : function()
31706     {
31707         return this.baseScale * Math.pow(1.1, this.scale);
31708     },
31709     
31710     onTouchStart : function(e)
31711     {
31712         if(!this.canvasLoaded){
31713             this.beforeSelectFile(e);
31714             return;
31715         }
31716         
31717         var touches = e.browserEvent.touches;
31718         
31719         if(!touches){
31720             return;
31721         }
31722         
31723         if(touches.length == 1){
31724             this.onMouseDown(e);
31725             return;
31726         }
31727         
31728         if(touches.length != 2){
31729             return;
31730         }
31731         
31732         var coords = [];
31733         
31734         for(var i = 0, finger; finger = touches[i]; i++){
31735             coords.push(finger.pageX, finger.pageY);
31736         }
31737         
31738         var x = Math.pow(coords[0] - coords[2], 2);
31739         var y = Math.pow(coords[1] - coords[3], 2);
31740         
31741         this.startDistance = Math.sqrt(x + y);
31742         
31743         this.startScale = this.scale;
31744         
31745         this.pinching = true;
31746         this.dragable = false;
31747         
31748     },
31749     
31750     onTouchMove : function(e)
31751     {
31752         if(!this.pinching && !this.dragable){
31753             return;
31754         }
31755         
31756         var touches = e.browserEvent.touches;
31757         
31758         if(!touches){
31759             return;
31760         }
31761         
31762         if(this.dragable){
31763             this.onMouseMove(e);
31764             return;
31765         }
31766         
31767         var coords = [];
31768         
31769         for(var i = 0, finger; finger = touches[i]; i++){
31770             coords.push(finger.pageX, finger.pageY);
31771         }
31772         
31773         var x = Math.pow(coords[0] - coords[2], 2);
31774         var y = Math.pow(coords[1] - coords[3], 2);
31775         
31776         this.endDistance = Math.sqrt(x + y);
31777         
31778         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31779         
31780         if(!this.zoomable()){
31781             this.scale = this.startScale;
31782             return;
31783         }
31784         
31785         this.draw();
31786         
31787     },
31788     
31789     onTouchEnd : function(e)
31790     {
31791         this.pinching = false;
31792         this.dragable = false;
31793         
31794     },
31795     
31796     process : function(file, crop)
31797     {
31798         if(this.loadMask){
31799             this.maskEl.mask(this.loadingText);
31800         }
31801         
31802         this.xhr = new XMLHttpRequest();
31803         
31804         file.xhr = this.xhr;
31805
31806         this.xhr.open(this.method, this.url, true);
31807         
31808         var headers = {
31809             "Accept": "application/json",
31810             "Cache-Control": "no-cache",
31811             "X-Requested-With": "XMLHttpRequest"
31812         };
31813         
31814         for (var headerName in headers) {
31815             var headerValue = headers[headerName];
31816             if (headerValue) {
31817                 this.xhr.setRequestHeader(headerName, headerValue);
31818             }
31819         }
31820         
31821         var _this = this;
31822         
31823         this.xhr.onload = function()
31824         {
31825             _this.xhrOnLoad(_this.xhr);
31826         }
31827         
31828         this.xhr.onerror = function()
31829         {
31830             _this.xhrOnError(_this.xhr);
31831         }
31832         
31833         var formData = new FormData();
31834
31835         formData.append('returnHTML', 'NO');
31836         
31837         if(crop){
31838             formData.append('crop', crop);
31839         }
31840         
31841         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31842             formData.append(this.paramName, file, file.name);
31843         }
31844         
31845         if(typeof(file.filename) != 'undefined'){
31846             formData.append('filename', file.filename);
31847         }
31848         
31849         if(typeof(file.mimetype) != 'undefined'){
31850             formData.append('mimetype', file.mimetype);
31851         }
31852         
31853         if(this.fireEvent('arrange', this, formData) != false){
31854             this.xhr.send(formData);
31855         };
31856     },
31857     
31858     xhrOnLoad : function(xhr)
31859     {
31860         if(this.loadMask){
31861             this.maskEl.unmask();
31862         }
31863         
31864         if (xhr.readyState !== 4) {
31865             this.fireEvent('exception', this, xhr);
31866             return;
31867         }
31868
31869         var response = Roo.decode(xhr.responseText);
31870         
31871         if(!response.success){
31872             this.fireEvent('exception', this, xhr);
31873             return;
31874         }
31875         
31876         var response = Roo.decode(xhr.responseText);
31877         
31878         this.fireEvent('upload', this, response);
31879         
31880     },
31881     
31882     xhrOnError : function()
31883     {
31884         if(this.loadMask){
31885             this.maskEl.unmask();
31886         }
31887         
31888         Roo.log('xhr on error');
31889         
31890         var response = Roo.decode(xhr.responseText);
31891           
31892         Roo.log(response);
31893         
31894     },
31895     
31896     prepare : function(file)
31897     {   
31898         if(this.loadMask){
31899             this.maskEl.mask(this.loadingText);
31900         }
31901         
31902         this.file = false;
31903         this.exif = {};
31904         
31905         if(typeof(file) === 'string'){
31906             this.loadCanvas(file);
31907             return;
31908         }
31909         
31910         if(!file || !this.urlAPI){
31911             return;
31912         }
31913         
31914         this.file = file;
31915         this.cropType = file.type;
31916         
31917         var _this = this;
31918         
31919         if(this.fireEvent('prepare', this, this.file) != false){
31920             
31921             var reader = new FileReader();
31922             
31923             reader.onload = function (e) {
31924                 if (e.target.error) {
31925                     Roo.log(e.target.error);
31926                     return;
31927                 }
31928                 
31929                 var buffer = e.target.result,
31930                     dataView = new DataView(buffer),
31931                     offset = 2,
31932                     maxOffset = dataView.byteLength - 4,
31933                     markerBytes,
31934                     markerLength;
31935                 
31936                 if (dataView.getUint16(0) === 0xffd8) {
31937                     while (offset < maxOffset) {
31938                         markerBytes = dataView.getUint16(offset);
31939                         
31940                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31941                             markerLength = dataView.getUint16(offset + 2) + 2;
31942                             if (offset + markerLength > dataView.byteLength) {
31943                                 Roo.log('Invalid meta data: Invalid segment size.');
31944                                 break;
31945                             }
31946                             
31947                             if(markerBytes == 0xffe1){
31948                                 _this.parseExifData(
31949                                     dataView,
31950                                     offset,
31951                                     markerLength
31952                                 );
31953                             }
31954                             
31955                             offset += markerLength;
31956                             
31957                             continue;
31958                         }
31959                         
31960                         break;
31961                     }
31962                     
31963                 }
31964                 
31965                 var url = _this.urlAPI.createObjectURL(_this.file);
31966                 
31967                 _this.loadCanvas(url);
31968                 
31969                 return;
31970             }
31971             
31972             reader.readAsArrayBuffer(this.file);
31973             
31974         }
31975         
31976     },
31977     
31978     parseExifData : function(dataView, offset, length)
31979     {
31980         var tiffOffset = offset + 10,
31981             littleEndian,
31982             dirOffset;
31983     
31984         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31985             // No Exif data, might be XMP data instead
31986             return;
31987         }
31988         
31989         // Check for the ASCII code for "Exif" (0x45786966):
31990         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31991             // No Exif data, might be XMP data instead
31992             return;
31993         }
31994         if (tiffOffset + 8 > dataView.byteLength) {
31995             Roo.log('Invalid Exif data: Invalid segment size.');
31996             return;
31997         }
31998         // Check for the two null bytes:
31999         if (dataView.getUint16(offset + 8) !== 0x0000) {
32000             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32001             return;
32002         }
32003         // Check the byte alignment:
32004         switch (dataView.getUint16(tiffOffset)) {
32005         case 0x4949:
32006             littleEndian = true;
32007             break;
32008         case 0x4D4D:
32009             littleEndian = false;
32010             break;
32011         default:
32012             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32013             return;
32014         }
32015         // Check for the TIFF tag marker (0x002A):
32016         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32017             Roo.log('Invalid Exif data: Missing TIFF marker.');
32018             return;
32019         }
32020         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32021         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32022         
32023         this.parseExifTags(
32024             dataView,
32025             tiffOffset,
32026             tiffOffset + dirOffset,
32027             littleEndian
32028         );
32029     },
32030     
32031     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32032     {
32033         var tagsNumber,
32034             dirEndOffset,
32035             i;
32036         if (dirOffset + 6 > dataView.byteLength) {
32037             Roo.log('Invalid Exif data: Invalid directory offset.');
32038             return;
32039         }
32040         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32041         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32042         if (dirEndOffset + 4 > dataView.byteLength) {
32043             Roo.log('Invalid Exif data: Invalid directory size.');
32044             return;
32045         }
32046         for (i = 0; i < tagsNumber; i += 1) {
32047             this.parseExifTag(
32048                 dataView,
32049                 tiffOffset,
32050                 dirOffset + 2 + 12 * i, // tag offset
32051                 littleEndian
32052             );
32053         }
32054         // Return the offset to the next directory:
32055         return dataView.getUint32(dirEndOffset, littleEndian);
32056     },
32057     
32058     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32059     {
32060         var tag = dataView.getUint16(offset, littleEndian);
32061         
32062         this.exif[tag] = this.getExifValue(
32063             dataView,
32064             tiffOffset,
32065             offset,
32066             dataView.getUint16(offset + 2, littleEndian), // tag type
32067             dataView.getUint32(offset + 4, littleEndian), // tag length
32068             littleEndian
32069         );
32070     },
32071     
32072     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32073     {
32074         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32075             tagSize,
32076             dataOffset,
32077             values,
32078             i,
32079             str,
32080             c;
32081     
32082         if (!tagType) {
32083             Roo.log('Invalid Exif data: Invalid tag type.');
32084             return;
32085         }
32086         
32087         tagSize = tagType.size * length;
32088         // Determine if the value is contained in the dataOffset bytes,
32089         // or if the value at the dataOffset is a pointer to the actual data:
32090         dataOffset = tagSize > 4 ?
32091                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32092         if (dataOffset + tagSize > dataView.byteLength) {
32093             Roo.log('Invalid Exif data: Invalid data offset.');
32094             return;
32095         }
32096         if (length === 1) {
32097             return tagType.getValue(dataView, dataOffset, littleEndian);
32098         }
32099         values = [];
32100         for (i = 0; i < length; i += 1) {
32101             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32102         }
32103         
32104         if (tagType.ascii) {
32105             str = '';
32106             // Concatenate the chars:
32107             for (i = 0; i < values.length; i += 1) {
32108                 c = values[i];
32109                 // Ignore the terminating NULL byte(s):
32110                 if (c === '\u0000') {
32111                     break;
32112                 }
32113                 str += c;
32114             }
32115             return str;
32116         }
32117         return values;
32118     }
32119     
32120 });
32121
32122 Roo.apply(Roo.bootstrap.UploadCropbox, {
32123     tags : {
32124         'Orientation': 0x0112
32125     },
32126     
32127     Orientation: {
32128             1: 0, //'top-left',
32129 //            2: 'top-right',
32130             3: 180, //'bottom-right',
32131 //            4: 'bottom-left',
32132 //            5: 'left-top',
32133             6: 90, //'right-top',
32134 //            7: 'right-bottom',
32135             8: 270 //'left-bottom'
32136     },
32137     
32138     exifTagTypes : {
32139         // byte, 8-bit unsigned int:
32140         1: {
32141             getValue: function (dataView, dataOffset) {
32142                 return dataView.getUint8(dataOffset);
32143             },
32144             size: 1
32145         },
32146         // ascii, 8-bit byte:
32147         2: {
32148             getValue: function (dataView, dataOffset) {
32149                 return String.fromCharCode(dataView.getUint8(dataOffset));
32150             },
32151             size: 1,
32152             ascii: true
32153         },
32154         // short, 16 bit int:
32155         3: {
32156             getValue: function (dataView, dataOffset, littleEndian) {
32157                 return dataView.getUint16(dataOffset, littleEndian);
32158             },
32159             size: 2
32160         },
32161         // long, 32 bit int:
32162         4: {
32163             getValue: function (dataView, dataOffset, littleEndian) {
32164                 return dataView.getUint32(dataOffset, littleEndian);
32165             },
32166             size: 4
32167         },
32168         // rational = two long values, first is numerator, second is denominator:
32169         5: {
32170             getValue: function (dataView, dataOffset, littleEndian) {
32171                 return dataView.getUint32(dataOffset, littleEndian) /
32172                     dataView.getUint32(dataOffset + 4, littleEndian);
32173             },
32174             size: 8
32175         },
32176         // slong, 32 bit signed int:
32177         9: {
32178             getValue: function (dataView, dataOffset, littleEndian) {
32179                 return dataView.getInt32(dataOffset, littleEndian);
32180             },
32181             size: 4
32182         },
32183         // srational, two slongs, first is numerator, second is denominator:
32184         10: {
32185             getValue: function (dataView, dataOffset, littleEndian) {
32186                 return dataView.getInt32(dataOffset, littleEndian) /
32187                     dataView.getInt32(dataOffset + 4, littleEndian);
32188             },
32189             size: 8
32190         }
32191     },
32192     
32193     footer : {
32194         STANDARD : [
32195             {
32196                 tag : 'div',
32197                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32198                 action : 'rotate-left',
32199                 cn : [
32200                     {
32201                         tag : 'button',
32202                         cls : 'btn btn-default',
32203                         html : '<i class="fa fa-undo"></i>'
32204                     }
32205                 ]
32206             },
32207             {
32208                 tag : 'div',
32209                 cls : 'btn-group roo-upload-cropbox-picture',
32210                 action : 'picture',
32211                 cn : [
32212                     {
32213                         tag : 'button',
32214                         cls : 'btn btn-default',
32215                         html : '<i class="fa fa-picture-o"></i>'
32216                     }
32217                 ]
32218             },
32219             {
32220                 tag : 'div',
32221                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32222                 action : 'rotate-right',
32223                 cn : [
32224                     {
32225                         tag : 'button',
32226                         cls : 'btn btn-default',
32227                         html : '<i class="fa fa-repeat"></i>'
32228                     }
32229                 ]
32230             }
32231         ],
32232         DOCUMENT : [
32233             {
32234                 tag : 'div',
32235                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32236                 action : 'rotate-left',
32237                 cn : [
32238                     {
32239                         tag : 'button',
32240                         cls : 'btn btn-default',
32241                         html : '<i class="fa fa-undo"></i>'
32242                     }
32243                 ]
32244             },
32245             {
32246                 tag : 'div',
32247                 cls : 'btn-group roo-upload-cropbox-download',
32248                 action : 'download',
32249                 cn : [
32250                     {
32251                         tag : 'button',
32252                         cls : 'btn btn-default',
32253                         html : '<i class="fa fa-download"></i>'
32254                     }
32255                 ]
32256             },
32257             {
32258                 tag : 'div',
32259                 cls : 'btn-group roo-upload-cropbox-crop',
32260                 action : 'crop',
32261                 cn : [
32262                     {
32263                         tag : 'button',
32264                         cls : 'btn btn-default',
32265                         html : '<i class="fa fa-crop"></i>'
32266                     }
32267                 ]
32268             },
32269             {
32270                 tag : 'div',
32271                 cls : 'btn-group roo-upload-cropbox-trash',
32272                 action : 'trash',
32273                 cn : [
32274                     {
32275                         tag : 'button',
32276                         cls : 'btn btn-default',
32277                         html : '<i class="fa fa-trash"></i>'
32278                     }
32279                 ]
32280             },
32281             {
32282                 tag : 'div',
32283                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32284                 action : 'rotate-right',
32285                 cn : [
32286                     {
32287                         tag : 'button',
32288                         cls : 'btn btn-default',
32289                         html : '<i class="fa fa-repeat"></i>'
32290                     }
32291                 ]
32292             }
32293         ],
32294         ROTATOR : [
32295             {
32296                 tag : 'div',
32297                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32298                 action : 'rotate-left',
32299                 cn : [
32300                     {
32301                         tag : 'button',
32302                         cls : 'btn btn-default',
32303                         html : '<i class="fa fa-undo"></i>'
32304                     }
32305                 ]
32306             },
32307             {
32308                 tag : 'div',
32309                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32310                 action : 'rotate-right',
32311                 cn : [
32312                     {
32313                         tag : 'button',
32314                         cls : 'btn btn-default',
32315                         html : '<i class="fa fa-repeat"></i>'
32316                     }
32317                 ]
32318             }
32319         ]
32320     }
32321 });
32322
32323 /*
32324 * Licence: LGPL
32325 */
32326
32327 /**
32328  * @class Roo.bootstrap.DocumentManager
32329  * @extends Roo.bootstrap.Component
32330  * Bootstrap DocumentManager class
32331  * @cfg {String} paramName default 'imageUpload'
32332  * @cfg {String} toolTipName default 'filename'
32333  * @cfg {String} method default POST
32334  * @cfg {String} url action url
32335  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32336  * @cfg {Boolean} multiple multiple upload default true
32337  * @cfg {Number} thumbSize default 300
32338  * @cfg {String} fieldLabel
32339  * @cfg {Number} labelWidth default 4
32340  * @cfg {String} labelAlign (left|top) default left
32341  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32342 * @cfg {Number} labellg set the width of label (1-12)
32343  * @cfg {Number} labelmd set the width of label (1-12)
32344  * @cfg {Number} labelsm set the width of label (1-12)
32345  * @cfg {Number} labelxs set the width of label (1-12)
32346  * 
32347  * @constructor
32348  * Create a new DocumentManager
32349  * @param {Object} config The config object
32350  */
32351
32352 Roo.bootstrap.DocumentManager = function(config){
32353     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32354     
32355     this.files = [];
32356     this.delegates = [];
32357     
32358     this.addEvents({
32359         /**
32360          * @event initial
32361          * Fire when initial the DocumentManager
32362          * @param {Roo.bootstrap.DocumentManager} this
32363          */
32364         "initial" : true,
32365         /**
32366          * @event inspect
32367          * inspect selected file
32368          * @param {Roo.bootstrap.DocumentManager} this
32369          * @param {File} file
32370          */
32371         "inspect" : true,
32372         /**
32373          * @event exception
32374          * Fire when xhr load exception
32375          * @param {Roo.bootstrap.DocumentManager} this
32376          * @param {XMLHttpRequest} xhr
32377          */
32378         "exception" : true,
32379         /**
32380          * @event afterupload
32381          * Fire when xhr load exception
32382          * @param {Roo.bootstrap.DocumentManager} this
32383          * @param {XMLHttpRequest} xhr
32384          */
32385         "afterupload" : true,
32386         /**
32387          * @event prepare
32388          * prepare the form data
32389          * @param {Roo.bootstrap.DocumentManager} this
32390          * @param {Object} formData
32391          */
32392         "prepare" : true,
32393         /**
32394          * @event remove
32395          * Fire when remove the file
32396          * @param {Roo.bootstrap.DocumentManager} this
32397          * @param {Object} file
32398          */
32399         "remove" : true,
32400         /**
32401          * @event refresh
32402          * Fire after refresh the file
32403          * @param {Roo.bootstrap.DocumentManager} this
32404          */
32405         "refresh" : true,
32406         /**
32407          * @event click
32408          * Fire after click the image
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          * @param {Object} file
32411          */
32412         "click" : true,
32413         /**
32414          * @event edit
32415          * Fire when upload a image and editable set to true
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          * @param {Object} file
32418          */
32419         "edit" : true,
32420         /**
32421          * @event beforeselectfile
32422          * Fire before select file
32423          * @param {Roo.bootstrap.DocumentManager} this
32424          */
32425         "beforeselectfile" : true,
32426         /**
32427          * @event process
32428          * Fire before process file
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {Object} file
32431          */
32432         "process" : true,
32433         /**
32434          * @event previewrendered
32435          * Fire when preview rendered
32436          * @param {Roo.bootstrap.DocumentManager} this
32437          * @param {Object} file
32438          */
32439         "previewrendered" : true,
32440         /**
32441          */
32442         "previewResize" : true
32443         
32444     });
32445 };
32446
32447 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32448     
32449     boxes : 0,
32450     inputName : '',
32451     thumbSize : 300,
32452     multiple : true,
32453     files : false,
32454     method : 'POST',
32455     url : '',
32456     paramName : 'imageUpload',
32457     toolTipName : 'filename',
32458     fieldLabel : '',
32459     labelWidth : 4,
32460     labelAlign : 'left',
32461     editable : true,
32462     delegates : false,
32463     xhr : false, 
32464     
32465     labellg : 0,
32466     labelmd : 0,
32467     labelsm : 0,
32468     labelxs : 0,
32469     
32470     getAutoCreate : function()
32471     {   
32472         var managerWidget = {
32473             tag : 'div',
32474             cls : 'roo-document-manager',
32475             cn : [
32476                 {
32477                     tag : 'input',
32478                     cls : 'roo-document-manager-selector',
32479                     type : 'file'
32480                 },
32481                 {
32482                     tag : 'div',
32483                     cls : 'roo-document-manager-uploader',
32484                     cn : [
32485                         {
32486                             tag : 'div',
32487                             cls : 'roo-document-manager-upload-btn',
32488                             html : '<i class="fa fa-plus"></i>'
32489                         }
32490                     ]
32491                     
32492                 }
32493             ]
32494         };
32495         
32496         var content = [
32497             {
32498                 tag : 'div',
32499                 cls : 'column col-md-12',
32500                 cn : managerWidget
32501             }
32502         ];
32503         
32504         if(this.fieldLabel.length){
32505             
32506             content = [
32507                 {
32508                     tag : 'div',
32509                     cls : 'column col-md-12',
32510                     html : this.fieldLabel
32511                 },
32512                 {
32513                     tag : 'div',
32514                     cls : 'column col-md-12',
32515                     cn : managerWidget
32516                 }
32517             ];
32518
32519             if(this.labelAlign == 'left'){
32520                 content = [
32521                     {
32522                         tag : 'div',
32523                         cls : 'column',
32524                         html : this.fieldLabel
32525                     },
32526                     {
32527                         tag : 'div',
32528                         cls : 'column',
32529                         cn : managerWidget
32530                     }
32531                 ];
32532                 
32533                 if(this.labelWidth > 12){
32534                     content[0].style = "width: " + this.labelWidth + 'px';
32535                 }
32536
32537                 if(this.labelWidth < 13 && this.labelmd == 0){
32538                     this.labelmd = this.labelWidth;
32539                 }
32540
32541                 if(this.labellg > 0){
32542                     content[0].cls += ' col-lg-' + this.labellg;
32543                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32544                 }
32545
32546                 if(this.labelmd > 0){
32547                     content[0].cls += ' col-md-' + this.labelmd;
32548                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32549                 }
32550
32551                 if(this.labelsm > 0){
32552                     content[0].cls += ' col-sm-' + this.labelsm;
32553                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32554                 }
32555
32556                 if(this.labelxs > 0){
32557                     content[0].cls += ' col-xs-' + this.labelxs;
32558                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32559                 }
32560                 
32561             }
32562         }
32563         
32564         var cfg = {
32565             tag : 'div',
32566             cls : 'row clearfix',
32567             cn : content
32568         };
32569         
32570         return cfg;
32571         
32572     },
32573     
32574     initEvents : function()
32575     {
32576         this.managerEl = this.el.select('.roo-document-manager', true).first();
32577         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32578         
32579         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32580         this.selectorEl.hide();
32581         
32582         if(this.multiple){
32583             this.selectorEl.attr('multiple', 'multiple');
32584         }
32585         
32586         this.selectorEl.on('change', this.onFileSelected, this);
32587         
32588         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32589         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32590         
32591         this.uploader.on('click', this.onUploaderClick, this);
32592         
32593         this.renderProgressDialog();
32594         
32595         var _this = this;
32596         
32597         window.addEventListener("resize", function() { _this.refresh(); } );
32598         
32599         this.fireEvent('initial', this);
32600     },
32601     
32602     renderProgressDialog : function()
32603     {
32604         var _this = this;
32605         
32606         this.progressDialog = new Roo.bootstrap.Modal({
32607             cls : 'roo-document-manager-progress-dialog',
32608             allow_close : false,
32609             animate : false,
32610             title : '',
32611             buttons : [
32612                 {
32613                     name  :'cancel',
32614                     weight : 'danger',
32615                     html : 'Cancel'
32616                 }
32617             ], 
32618             listeners : { 
32619                 btnclick : function() {
32620                     _this.uploadCancel();
32621                     this.hide();
32622                 }
32623             }
32624         });
32625          
32626         this.progressDialog.render(Roo.get(document.body));
32627          
32628         this.progress = new Roo.bootstrap.Progress({
32629             cls : 'roo-document-manager-progress',
32630             active : true,
32631             striped : true
32632         });
32633         
32634         this.progress.render(this.progressDialog.getChildContainer());
32635         
32636         this.progressBar = new Roo.bootstrap.ProgressBar({
32637             cls : 'roo-document-manager-progress-bar',
32638             aria_valuenow : 0,
32639             aria_valuemin : 0,
32640             aria_valuemax : 12,
32641             panel : 'success'
32642         });
32643         
32644         this.progressBar.render(this.progress.getChildContainer());
32645     },
32646     
32647     onUploaderClick : function(e)
32648     {
32649         e.preventDefault();
32650      
32651         if(this.fireEvent('beforeselectfile', this) != false){
32652             this.selectorEl.dom.click();
32653         }
32654         
32655     },
32656     
32657     onFileSelected : function(e)
32658     {
32659         e.preventDefault();
32660         
32661         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32662             return;
32663         }
32664         
32665         Roo.each(this.selectorEl.dom.files, function(file){
32666             if(this.fireEvent('inspect', this, file) != false){
32667                 this.files.push(file);
32668             }
32669         }, this);
32670         
32671         this.queue();
32672         
32673     },
32674     
32675     queue : function()
32676     {
32677         this.selectorEl.dom.value = '';
32678         
32679         if(!this.files || !this.files.length){
32680             return;
32681         }
32682         
32683         if(this.boxes > 0 && this.files.length > this.boxes){
32684             this.files = this.files.slice(0, this.boxes);
32685         }
32686         
32687         this.uploader.show();
32688         
32689         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32690             this.uploader.hide();
32691         }
32692         
32693         var _this = this;
32694         
32695         var files = [];
32696         
32697         var docs = [];
32698         
32699         Roo.each(this.files, function(file){
32700             
32701             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32702                 var f = this.renderPreview(file);
32703                 files.push(f);
32704                 return;
32705             }
32706             
32707             if(file.type.indexOf('image') != -1){
32708                 this.delegates.push(
32709                     (function(){
32710                         _this.process(file);
32711                     }).createDelegate(this)
32712                 );
32713         
32714                 return;
32715             }
32716             
32717             docs.push(
32718                 (function(){
32719                     _this.process(file);
32720                 }).createDelegate(this)
32721             );
32722             
32723         }, this);
32724         
32725         this.files = files;
32726         
32727         this.delegates = this.delegates.concat(docs);
32728         
32729         if(!this.delegates.length){
32730             this.refresh();
32731             return;
32732         }
32733         
32734         this.progressBar.aria_valuemax = this.delegates.length;
32735         
32736         this.arrange();
32737         
32738         return;
32739     },
32740     
32741     arrange : function()
32742     {
32743         if(!this.delegates.length){
32744             this.progressDialog.hide();
32745             this.refresh();
32746             return;
32747         }
32748         
32749         var delegate = this.delegates.shift();
32750         
32751         this.progressDialog.show();
32752         
32753         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32754         
32755         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32756         
32757         delegate();
32758     },
32759     
32760     refresh : function()
32761     {
32762         this.uploader.show();
32763         
32764         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32765             this.uploader.hide();
32766         }
32767         
32768         Roo.isTouch ? this.closable(false) : this.closable(true);
32769         
32770         this.fireEvent('refresh', this);
32771     },
32772     
32773     onRemove : function(e, el, o)
32774     {
32775         e.preventDefault();
32776         
32777         this.fireEvent('remove', this, o);
32778         
32779     },
32780     
32781     remove : function(o)
32782     {
32783         var files = [];
32784         
32785         Roo.each(this.files, function(file){
32786             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32787                 files.push(file);
32788                 return;
32789             }
32790
32791             o.target.remove();
32792
32793         }, this);
32794         
32795         this.files = files;
32796         
32797         this.refresh();
32798     },
32799     
32800     clear : function()
32801     {
32802         Roo.each(this.files, function(file){
32803             if(!file.target){
32804                 return;
32805             }
32806             
32807             file.target.remove();
32808
32809         }, this);
32810         
32811         this.files = [];
32812         
32813         this.refresh();
32814     },
32815     
32816     onClick : function(e, el, o)
32817     {
32818         e.preventDefault();
32819         
32820         this.fireEvent('click', this, o);
32821         
32822     },
32823     
32824     closable : function(closable)
32825     {
32826         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32827             
32828             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32829             
32830             if(closable){
32831                 el.show();
32832                 return;
32833             }
32834             
32835             el.hide();
32836             
32837         }, this);
32838     },
32839     
32840     xhrOnLoad : function(xhr)
32841     {
32842         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32843             el.remove();
32844         }, this);
32845         
32846         if (xhr.readyState !== 4) {
32847             this.arrange();
32848             this.fireEvent('exception', this, xhr);
32849             return;
32850         }
32851
32852         var response = Roo.decode(xhr.responseText);
32853         
32854         if(!response.success){
32855             this.arrange();
32856             this.fireEvent('exception', this, xhr);
32857             return;
32858         }
32859         
32860         var file = this.renderPreview(response.data);
32861         
32862         this.files.push(file);
32863         
32864         this.arrange();
32865         
32866         this.fireEvent('afterupload', this, xhr);
32867         
32868     },
32869     
32870     xhrOnError : function(xhr)
32871     {
32872         Roo.log('xhr on error');
32873         
32874         var response = Roo.decode(xhr.responseText);
32875           
32876         Roo.log(response);
32877         
32878         this.arrange();
32879     },
32880     
32881     process : function(file)
32882     {
32883         if(this.fireEvent('process', this, file) !== false){
32884             if(this.editable && file.type.indexOf('image') != -1){
32885                 this.fireEvent('edit', this, file);
32886                 return;
32887             }
32888
32889             this.uploadStart(file, false);
32890
32891             return;
32892         }
32893         
32894     },
32895     
32896     uploadStart : function(file, crop)
32897     {
32898         this.xhr = new XMLHttpRequest();
32899         
32900         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32901             this.arrange();
32902             return;
32903         }
32904         
32905         file.xhr = this.xhr;
32906             
32907         this.managerEl.createChild({
32908             tag : 'div',
32909             cls : 'roo-document-manager-loading',
32910             cn : [
32911                 {
32912                     tag : 'div',
32913                     tooltip : file.name,
32914                     cls : 'roo-document-manager-thumb',
32915                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32916                 }
32917             ]
32918
32919         });
32920
32921         this.xhr.open(this.method, this.url, true);
32922         
32923         var headers = {
32924             "Accept": "application/json",
32925             "Cache-Control": "no-cache",
32926             "X-Requested-With": "XMLHttpRequest"
32927         };
32928         
32929         for (var headerName in headers) {
32930             var headerValue = headers[headerName];
32931             if (headerValue) {
32932                 this.xhr.setRequestHeader(headerName, headerValue);
32933             }
32934         }
32935         
32936         var _this = this;
32937         
32938         this.xhr.onload = function()
32939         {
32940             _this.xhrOnLoad(_this.xhr);
32941         }
32942         
32943         this.xhr.onerror = function()
32944         {
32945             _this.xhrOnError(_this.xhr);
32946         }
32947         
32948         var formData = new FormData();
32949
32950         formData.append('returnHTML', 'NO');
32951         
32952         if(crop){
32953             formData.append('crop', crop);
32954         }
32955         
32956         formData.append(this.paramName, file, file.name);
32957         
32958         var options = {
32959             file : file, 
32960             manually : false
32961         };
32962         
32963         if(this.fireEvent('prepare', this, formData, options) != false){
32964             
32965             if(options.manually){
32966                 return;
32967             }
32968             
32969             this.xhr.send(formData);
32970             return;
32971         };
32972         
32973         this.uploadCancel();
32974     },
32975     
32976     uploadCancel : function()
32977     {
32978         if (this.xhr) {
32979             this.xhr.abort();
32980         }
32981         
32982         this.delegates = [];
32983         
32984         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32985             el.remove();
32986         }, this);
32987         
32988         this.arrange();
32989     },
32990     
32991     renderPreview : function(file)
32992     {
32993         if(typeof(file.target) != 'undefined' && file.target){
32994             return file;
32995         }
32996         
32997         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32998         
32999         var previewEl = this.managerEl.createChild({
33000             tag : 'div',
33001             cls : 'roo-document-manager-preview',
33002             cn : [
33003                 {
33004                     tag : 'div',
33005                     tooltip : file[this.toolTipName],
33006                     cls : 'roo-document-manager-thumb',
33007                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33008                 },
33009                 {
33010                     tag : 'button',
33011                     cls : 'close',
33012                     html : '<i class="fa fa-times-circle"></i>'
33013                 }
33014             ]
33015         });
33016
33017         var close = previewEl.select('button.close', true).first();
33018
33019         close.on('click', this.onRemove, this, file);
33020
33021         file.target = previewEl;
33022
33023         var image = previewEl.select('img', true).first();
33024         
33025         var _this = this;
33026         
33027         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33028         
33029         image.on('click', this.onClick, this, file);
33030         
33031         this.fireEvent('previewrendered', this, file);
33032         
33033         return file;
33034         
33035     },
33036     
33037     onPreviewLoad : function(file, image)
33038     {
33039         if(typeof(file.target) == 'undefined' || !file.target){
33040             return;
33041         }
33042         
33043         var width = image.dom.naturalWidth || image.dom.width;
33044         var height = image.dom.naturalHeight || image.dom.height;
33045         
33046         if(!this.previewResize) {
33047             return;
33048         }
33049         
33050         if(width > height){
33051             file.target.addClass('wide');
33052             return;
33053         }
33054         
33055         file.target.addClass('tall');
33056         return;
33057         
33058     },
33059     
33060     uploadFromSource : function(file, crop)
33061     {
33062         this.xhr = new XMLHttpRequest();
33063         
33064         this.managerEl.createChild({
33065             tag : 'div',
33066             cls : 'roo-document-manager-loading',
33067             cn : [
33068                 {
33069                     tag : 'div',
33070                     tooltip : file.name,
33071                     cls : 'roo-document-manager-thumb',
33072                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33073                 }
33074             ]
33075
33076         });
33077
33078         this.xhr.open(this.method, this.url, true);
33079         
33080         var headers = {
33081             "Accept": "application/json",
33082             "Cache-Control": "no-cache",
33083             "X-Requested-With": "XMLHttpRequest"
33084         };
33085         
33086         for (var headerName in headers) {
33087             var headerValue = headers[headerName];
33088             if (headerValue) {
33089                 this.xhr.setRequestHeader(headerName, headerValue);
33090             }
33091         }
33092         
33093         var _this = this;
33094         
33095         this.xhr.onload = function()
33096         {
33097             _this.xhrOnLoad(_this.xhr);
33098         }
33099         
33100         this.xhr.onerror = function()
33101         {
33102             _this.xhrOnError(_this.xhr);
33103         }
33104         
33105         var formData = new FormData();
33106
33107         formData.append('returnHTML', 'NO');
33108         
33109         formData.append('crop', crop);
33110         
33111         if(typeof(file.filename) != 'undefined'){
33112             formData.append('filename', file.filename);
33113         }
33114         
33115         if(typeof(file.mimetype) != 'undefined'){
33116             formData.append('mimetype', file.mimetype);
33117         }
33118         
33119         Roo.log(formData);
33120         
33121         if(this.fireEvent('prepare', this, formData) != false){
33122             this.xhr.send(formData);
33123         };
33124     }
33125 });
33126
33127 /*
33128 * Licence: LGPL
33129 */
33130
33131 /**
33132  * @class Roo.bootstrap.DocumentViewer
33133  * @extends Roo.bootstrap.Component
33134  * Bootstrap DocumentViewer class
33135  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33136  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33137  * 
33138  * @constructor
33139  * Create a new DocumentViewer
33140  * @param {Object} config The config object
33141  */
33142
33143 Roo.bootstrap.DocumentViewer = function(config){
33144     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33145     
33146     this.addEvents({
33147         /**
33148          * @event initial
33149          * Fire after initEvent
33150          * @param {Roo.bootstrap.DocumentViewer} this
33151          */
33152         "initial" : true,
33153         /**
33154          * @event click
33155          * Fire after click
33156          * @param {Roo.bootstrap.DocumentViewer} this
33157          */
33158         "click" : true,
33159         /**
33160          * @event download
33161          * Fire after download button
33162          * @param {Roo.bootstrap.DocumentViewer} this
33163          */
33164         "download" : true,
33165         /**
33166          * @event trash
33167          * Fire after trash button
33168          * @param {Roo.bootstrap.DocumentViewer} this
33169          */
33170         "trash" : true
33171         
33172     });
33173 };
33174
33175 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33176     
33177     showDownload : true,
33178     
33179     showTrash : true,
33180     
33181     getAutoCreate : function()
33182     {
33183         var cfg = {
33184             tag : 'div',
33185             cls : 'roo-document-viewer',
33186             cn : [
33187                 {
33188                     tag : 'div',
33189                     cls : 'roo-document-viewer-body',
33190                     cn : [
33191                         {
33192                             tag : 'div',
33193                             cls : 'roo-document-viewer-thumb',
33194                             cn : [
33195                                 {
33196                                     tag : 'img',
33197                                     cls : 'roo-document-viewer-image'
33198                                 }
33199                             ]
33200                         }
33201                     ]
33202                 },
33203                 {
33204                     tag : 'div',
33205                     cls : 'roo-document-viewer-footer',
33206                     cn : {
33207                         tag : 'div',
33208                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33209                         cn : [
33210                             {
33211                                 tag : 'div',
33212                                 cls : 'btn-group roo-document-viewer-download',
33213                                 cn : [
33214                                     {
33215                                         tag : 'button',
33216                                         cls : 'btn btn-default',
33217                                         html : '<i class="fa fa-download"></i>'
33218                                     }
33219                                 ]
33220                             },
33221                             {
33222                                 tag : 'div',
33223                                 cls : 'btn-group roo-document-viewer-trash',
33224                                 cn : [
33225                                     {
33226                                         tag : 'button',
33227                                         cls : 'btn btn-default',
33228                                         html : '<i class="fa fa-trash"></i>'
33229                                     }
33230                                 ]
33231                             }
33232                         ]
33233                     }
33234                 }
33235             ]
33236         };
33237         
33238         return cfg;
33239     },
33240     
33241     initEvents : function()
33242     {
33243         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33244         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33247         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33248         
33249         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33250         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33251         
33252         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33253         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33254         
33255         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33256         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33257         
33258         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33259         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33260         
33261         this.bodyEl.on('click', this.onClick, this);
33262         this.downloadBtn.on('click', this.onDownload, this);
33263         this.trashBtn.on('click', this.onTrash, this);
33264         
33265         this.downloadBtn.hide();
33266         this.trashBtn.hide();
33267         
33268         if(this.showDownload){
33269             this.downloadBtn.show();
33270         }
33271         
33272         if(this.showTrash){
33273             this.trashBtn.show();
33274         }
33275         
33276         if(!this.showDownload && !this.showTrash) {
33277             this.footerEl.hide();
33278         }
33279         
33280     },
33281     
33282     initial : function()
33283     {
33284         this.fireEvent('initial', this);
33285         
33286     },
33287     
33288     onClick : function(e)
33289     {
33290         e.preventDefault();
33291         
33292         this.fireEvent('click', this);
33293     },
33294     
33295     onDownload : function(e)
33296     {
33297         e.preventDefault();
33298         
33299         this.fireEvent('download', this);
33300     },
33301     
33302     onTrash : function(e)
33303     {
33304         e.preventDefault();
33305         
33306         this.fireEvent('trash', this);
33307     }
33308     
33309 });
33310 /*
33311  * - LGPL
33312  *
33313  * nav progress bar
33314  * 
33315  */
33316
33317 /**
33318  * @class Roo.bootstrap.NavProgressBar
33319  * @extends Roo.bootstrap.Component
33320  * Bootstrap NavProgressBar class
33321  * 
33322  * @constructor
33323  * Create a new nav progress bar
33324  * @param {Object} config The config object
33325  */
33326
33327 Roo.bootstrap.NavProgressBar = function(config){
33328     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33329
33330     this.bullets = this.bullets || [];
33331    
33332 //    Roo.bootstrap.NavProgressBar.register(this);
33333      this.addEvents({
33334         /**
33335              * @event changed
33336              * Fires when the active item changes
33337              * @param {Roo.bootstrap.NavProgressBar} this
33338              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33339              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33340          */
33341         'changed': true
33342      });
33343     
33344 };
33345
33346 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33347     
33348     bullets : [],
33349     barItems : [],
33350     
33351     getAutoCreate : function()
33352     {
33353         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33354         
33355         cfg = {
33356             tag : 'div',
33357             cls : 'roo-navigation-bar-group',
33358             cn : [
33359                 {
33360                     tag : 'div',
33361                     cls : 'roo-navigation-top-bar'
33362                 },
33363                 {
33364                     tag : 'div',
33365                     cls : 'roo-navigation-bullets-bar',
33366                     cn : [
33367                         {
33368                             tag : 'ul',
33369                             cls : 'roo-navigation-bar'
33370                         }
33371                     ]
33372                 },
33373                 
33374                 {
33375                     tag : 'div',
33376                     cls : 'roo-navigation-bottom-bar'
33377                 }
33378             ]
33379             
33380         };
33381         
33382         return cfg;
33383         
33384     },
33385     
33386     initEvents: function() 
33387     {
33388         
33389     },
33390     
33391     onRender : function(ct, position) 
33392     {
33393         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33394         
33395         if(this.bullets.length){
33396             Roo.each(this.bullets, function(b){
33397                this.addItem(b);
33398             }, this);
33399         }
33400         
33401         this.format();
33402         
33403     },
33404     
33405     addItem : function(cfg)
33406     {
33407         var item = new Roo.bootstrap.NavProgressItem(cfg);
33408         
33409         item.parentId = this.id;
33410         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33411         
33412         if(cfg.html){
33413             var top = new Roo.bootstrap.Element({
33414                 tag : 'div',
33415                 cls : 'roo-navigation-bar-text'
33416             });
33417             
33418             var bottom = new Roo.bootstrap.Element({
33419                 tag : 'div',
33420                 cls : 'roo-navigation-bar-text'
33421             });
33422             
33423             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33424             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33425             
33426             var topText = new Roo.bootstrap.Element({
33427                 tag : 'span',
33428                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33429             });
33430             
33431             var bottomText = new Roo.bootstrap.Element({
33432                 tag : 'span',
33433                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33434             });
33435             
33436             topText.onRender(top.el, null);
33437             bottomText.onRender(bottom.el, null);
33438             
33439             item.topEl = top;
33440             item.bottomEl = bottom;
33441         }
33442         
33443         this.barItems.push(item);
33444         
33445         return item;
33446     },
33447     
33448     getActive : function()
33449     {
33450         var active = false;
33451         
33452         Roo.each(this.barItems, function(v){
33453             
33454             if (!v.isActive()) {
33455                 return;
33456             }
33457             
33458             active = v;
33459             return false;
33460             
33461         });
33462         
33463         return active;
33464     },
33465     
33466     setActiveItem : function(item)
33467     {
33468         var prev = false;
33469         
33470         Roo.each(this.barItems, function(v){
33471             if (v.rid == item.rid) {
33472                 return ;
33473             }
33474             
33475             if (v.isActive()) {
33476                 v.setActive(false);
33477                 prev = v;
33478             }
33479         });
33480
33481         item.setActive(true);
33482         
33483         this.fireEvent('changed', this, item, prev);
33484     },
33485     
33486     getBarItem: function(rid)
33487     {
33488         var ret = false;
33489         
33490         Roo.each(this.barItems, function(e) {
33491             if (e.rid != rid) {
33492                 return;
33493             }
33494             
33495             ret =  e;
33496             return false;
33497         });
33498         
33499         return ret;
33500     },
33501     
33502     indexOfItem : function(item)
33503     {
33504         var index = false;
33505         
33506         Roo.each(this.barItems, function(v, i){
33507             
33508             if (v.rid != item.rid) {
33509                 return;
33510             }
33511             
33512             index = i;
33513             return false
33514         });
33515         
33516         return index;
33517     },
33518     
33519     setActiveNext : function()
33520     {
33521         var i = this.indexOfItem(this.getActive());
33522         
33523         if (i > this.barItems.length) {
33524             return;
33525         }
33526         
33527         this.setActiveItem(this.barItems[i+1]);
33528     },
33529     
33530     setActivePrev : function()
33531     {
33532         var i = this.indexOfItem(this.getActive());
33533         
33534         if (i  < 1) {
33535             return;
33536         }
33537         
33538         this.setActiveItem(this.barItems[i-1]);
33539     },
33540     
33541     format : function()
33542     {
33543         if(!this.barItems.length){
33544             return;
33545         }
33546      
33547         var width = 100 / this.barItems.length;
33548         
33549         Roo.each(this.barItems, function(i){
33550             i.el.setStyle('width', width + '%');
33551             i.topEl.el.setStyle('width', width + '%');
33552             i.bottomEl.el.setStyle('width', width + '%');
33553         }, this);
33554         
33555     }
33556     
33557 });
33558 /*
33559  * - LGPL
33560  *
33561  * Nav Progress Item
33562  * 
33563  */
33564
33565 /**
33566  * @class Roo.bootstrap.NavProgressItem
33567  * @extends Roo.bootstrap.Component
33568  * Bootstrap NavProgressItem class
33569  * @cfg {String} rid the reference id
33570  * @cfg {Boolean} active (true|false) Is item active default false
33571  * @cfg {Boolean} disabled (true|false) Is item active default false
33572  * @cfg {String} html
33573  * @cfg {String} position (top|bottom) text position default bottom
33574  * @cfg {String} icon show icon instead of number
33575  * 
33576  * @constructor
33577  * Create a new NavProgressItem
33578  * @param {Object} config The config object
33579  */
33580 Roo.bootstrap.NavProgressItem = function(config){
33581     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33582     this.addEvents({
33583         // raw events
33584         /**
33585          * @event click
33586          * The raw click event for the entire grid.
33587          * @param {Roo.bootstrap.NavProgressItem} this
33588          * @param {Roo.EventObject} e
33589          */
33590         "click" : true
33591     });
33592    
33593 };
33594
33595 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33596     
33597     rid : '',
33598     active : false,
33599     disabled : false,
33600     html : '',
33601     position : 'bottom',
33602     icon : false,
33603     
33604     getAutoCreate : function()
33605     {
33606         var iconCls = 'roo-navigation-bar-item-icon';
33607         
33608         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33609         
33610         var cfg = {
33611             tag: 'li',
33612             cls: 'roo-navigation-bar-item',
33613             cn : [
33614                 {
33615                     tag : 'i',
33616                     cls : iconCls
33617                 }
33618             ]
33619         };
33620         
33621         if(this.active){
33622             cfg.cls += ' active';
33623         }
33624         if(this.disabled){
33625             cfg.cls += ' disabled';
33626         }
33627         
33628         return cfg;
33629     },
33630     
33631     disable : function()
33632     {
33633         this.setDisabled(true);
33634     },
33635     
33636     enable : function()
33637     {
33638         this.setDisabled(false);
33639     },
33640     
33641     initEvents: function() 
33642     {
33643         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33644         
33645         this.iconEl.on('click', this.onClick, this);
33646     },
33647     
33648     onClick : function(e)
33649     {
33650         e.preventDefault();
33651         
33652         if(this.disabled){
33653             return;
33654         }
33655         
33656         if(this.fireEvent('click', this, e) === false){
33657             return;
33658         };
33659         
33660         this.parent().setActiveItem(this);
33661     },
33662     
33663     isActive: function () 
33664     {
33665         return this.active;
33666     },
33667     
33668     setActive : function(state)
33669     {
33670         if(this.active == state){
33671             return;
33672         }
33673         
33674         this.active = state;
33675         
33676         if (state) {
33677             this.el.addClass('active');
33678             return;
33679         }
33680         
33681         this.el.removeClass('active');
33682         
33683         return;
33684     },
33685     
33686     setDisabled : function(state)
33687     {
33688         if(this.disabled == state){
33689             return;
33690         }
33691         
33692         this.disabled = state;
33693         
33694         if (state) {
33695             this.el.addClass('disabled');
33696             return;
33697         }
33698         
33699         this.el.removeClass('disabled');
33700     },
33701     
33702     tooltipEl : function()
33703     {
33704         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33705     }
33706 });
33707  
33708
33709  /*
33710  * - LGPL
33711  *
33712  * FieldLabel
33713  * 
33714  */
33715
33716 /**
33717  * @class Roo.bootstrap.FieldLabel
33718  * @extends Roo.bootstrap.Component
33719  * Bootstrap FieldLabel class
33720  * @cfg {String} html contents of the element
33721  * @cfg {String} tag tag of the element default label
33722  * @cfg {String} cls class of the element
33723  * @cfg {String} target label target 
33724  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33725  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33726  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33727  * @cfg {String} iconTooltip default "This field is required"
33728  * @cfg {String} indicatorpos (left|right) default left
33729  * 
33730  * @constructor
33731  * Create a new FieldLabel
33732  * @param {Object} config The config object
33733  */
33734
33735 Roo.bootstrap.FieldLabel = function(config){
33736     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33737     
33738     this.addEvents({
33739             /**
33740              * @event invalid
33741              * Fires after the field has been marked as invalid.
33742              * @param {Roo.form.FieldLabel} this
33743              * @param {String} msg The validation message
33744              */
33745             invalid : true,
33746             /**
33747              * @event valid
33748              * Fires after the field has been validated with no errors.
33749              * @param {Roo.form.FieldLabel} this
33750              */
33751             valid : true
33752         });
33753 };
33754
33755 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33756     
33757     tag: 'label',
33758     cls: '',
33759     html: '',
33760     target: '',
33761     allowBlank : true,
33762     invalidClass : 'has-warning',
33763     validClass : 'has-success',
33764     iconTooltip : 'This field is required',
33765     indicatorpos : 'left',
33766     
33767     getAutoCreate : function(){
33768         
33769         var cls = "";
33770         if (!this.allowBlank) {
33771             cls  = "visible";
33772         }
33773         
33774         var cfg = {
33775             tag : this.tag,
33776             cls : 'roo-bootstrap-field-label ' + this.cls,
33777             for : this.target,
33778             cn : [
33779                 {
33780                     tag : 'i',
33781                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33782                     tooltip : this.iconTooltip
33783                 },
33784                 {
33785                     tag : 'span',
33786                     html : this.html
33787                 }
33788             ] 
33789         };
33790         
33791         if(this.indicatorpos == 'right'){
33792             var cfg = {
33793                 tag : this.tag,
33794                 cls : 'roo-bootstrap-field-label ' + this.cls,
33795                 for : this.target,
33796                 cn : [
33797                     {
33798                         tag : 'span',
33799                         html : this.html
33800                     },
33801                     {
33802                         tag : 'i',
33803                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33804                         tooltip : this.iconTooltip
33805                     }
33806                 ] 
33807             };
33808         }
33809         
33810         return cfg;
33811     },
33812     
33813     initEvents: function() 
33814     {
33815         Roo.bootstrap.Element.superclass.initEvents.call(this);
33816         
33817         this.indicator = this.indicatorEl();
33818         
33819         if(this.indicator){
33820             this.indicator.removeClass('visible');
33821             this.indicator.addClass('invisible');
33822         }
33823         
33824         Roo.bootstrap.FieldLabel.register(this);
33825     },
33826     
33827     indicatorEl : function()
33828     {
33829         var indicator = this.el.select('i.roo-required-indicator',true).first();
33830         
33831         if(!indicator){
33832             return false;
33833         }
33834         
33835         return indicator;
33836         
33837     },
33838     
33839     /**
33840      * Mark this field as valid
33841      */
33842     markValid : function()
33843     {
33844         if(this.indicator){
33845             this.indicator.removeClass('visible');
33846             this.indicator.addClass('invisible');
33847         }
33848         if (Roo.bootstrap.version == 3) {
33849             this.el.removeClass(this.invalidClass);
33850             this.el.addClass(this.validClass);
33851         } else {
33852             this.el.removeClass('is-invalid');
33853             this.el.addClass('is-valid');
33854         }
33855         
33856         
33857         this.fireEvent('valid', this);
33858     },
33859     
33860     /**
33861      * Mark this field as invalid
33862      * @param {String} msg The validation message
33863      */
33864     markInvalid : function(msg)
33865     {
33866         if(this.indicator){
33867             this.indicator.removeClass('invisible');
33868             this.indicator.addClass('visible');
33869         }
33870           if (Roo.bootstrap.version == 3) {
33871             this.el.removeClass(this.validClass);
33872             this.el.addClass(this.invalidClass);
33873         } else {
33874             this.el.removeClass('is-valid');
33875             this.el.addClass('is-invalid');
33876         }
33877         
33878         
33879         this.fireEvent('invalid', this, msg);
33880     }
33881     
33882    
33883 });
33884
33885 Roo.apply(Roo.bootstrap.FieldLabel, {
33886     
33887     groups: {},
33888     
33889      /**
33890     * register a FieldLabel Group
33891     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33892     */
33893     register : function(label)
33894     {
33895         if(this.groups.hasOwnProperty(label.target)){
33896             return;
33897         }
33898      
33899         this.groups[label.target] = label;
33900         
33901     },
33902     /**
33903     * fetch a FieldLabel Group based on the target
33904     * @param {string} target
33905     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33906     */
33907     get: function(target) {
33908         if (typeof(this.groups[target]) == 'undefined') {
33909             return false;
33910         }
33911         
33912         return this.groups[target] ;
33913     }
33914 });
33915
33916  
33917
33918  /*
33919  * - LGPL
33920  *
33921  * page DateSplitField.
33922  * 
33923  */
33924
33925
33926 /**
33927  * @class Roo.bootstrap.DateSplitField
33928  * @extends Roo.bootstrap.Component
33929  * Bootstrap DateSplitField class
33930  * @cfg {string} fieldLabel - the label associated
33931  * @cfg {Number} labelWidth set the width of label (0-12)
33932  * @cfg {String} labelAlign (top|left)
33933  * @cfg {Boolean} dayAllowBlank (true|false) default false
33934  * @cfg {Boolean} monthAllowBlank (true|false) default false
33935  * @cfg {Boolean} yearAllowBlank (true|false) default false
33936  * @cfg {string} dayPlaceholder 
33937  * @cfg {string} monthPlaceholder
33938  * @cfg {string} yearPlaceholder
33939  * @cfg {string} dayFormat default 'd'
33940  * @cfg {string} monthFormat default 'm'
33941  * @cfg {string} yearFormat default 'Y'
33942  * @cfg {Number} labellg set the width of label (1-12)
33943  * @cfg {Number} labelmd set the width of label (1-12)
33944  * @cfg {Number} labelsm set the width of label (1-12)
33945  * @cfg {Number} labelxs set the width of label (1-12)
33946
33947  *     
33948  * @constructor
33949  * Create a new DateSplitField
33950  * @param {Object} config The config object
33951  */
33952
33953 Roo.bootstrap.DateSplitField = function(config){
33954     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33955     
33956     this.addEvents({
33957         // raw events
33958          /**
33959          * @event years
33960          * getting the data of years
33961          * @param {Roo.bootstrap.DateSplitField} this
33962          * @param {Object} years
33963          */
33964         "years" : true,
33965         /**
33966          * @event days
33967          * getting the data of days
33968          * @param {Roo.bootstrap.DateSplitField} this
33969          * @param {Object} days
33970          */
33971         "days" : true,
33972         /**
33973          * @event invalid
33974          * Fires after the field has been marked as invalid.
33975          * @param {Roo.form.Field} this
33976          * @param {String} msg The validation message
33977          */
33978         invalid : true,
33979        /**
33980          * @event valid
33981          * Fires after the field has been validated with no errors.
33982          * @param {Roo.form.Field} this
33983          */
33984         valid : true
33985     });
33986 };
33987
33988 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33989     
33990     fieldLabel : '',
33991     labelAlign : 'top',
33992     labelWidth : 3,
33993     dayAllowBlank : false,
33994     monthAllowBlank : false,
33995     yearAllowBlank : false,
33996     dayPlaceholder : '',
33997     monthPlaceholder : '',
33998     yearPlaceholder : '',
33999     dayFormat : 'd',
34000     monthFormat : 'm',
34001     yearFormat : 'Y',
34002     isFormField : true,
34003     labellg : 0,
34004     labelmd : 0,
34005     labelsm : 0,
34006     labelxs : 0,
34007     
34008     getAutoCreate : function()
34009     {
34010         var cfg = {
34011             tag : 'div',
34012             cls : 'row roo-date-split-field-group',
34013             cn : [
34014                 {
34015                     tag : 'input',
34016                     type : 'hidden',
34017                     cls : 'form-hidden-field roo-date-split-field-group-value',
34018                     name : this.name
34019                 }
34020             ]
34021         };
34022         
34023         var labelCls = 'col-md-12';
34024         var contentCls = 'col-md-4';
34025         
34026         if(this.fieldLabel){
34027             
34028             var label = {
34029                 tag : 'div',
34030                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34031                 cn : [
34032                     {
34033                         tag : 'label',
34034                         html : this.fieldLabel
34035                     }
34036                 ]
34037             };
34038             
34039             if(this.labelAlign == 'left'){
34040             
34041                 if(this.labelWidth > 12){
34042                     label.style = "width: " + this.labelWidth + 'px';
34043                 }
34044
34045                 if(this.labelWidth < 13 && this.labelmd == 0){
34046                     this.labelmd = this.labelWidth;
34047                 }
34048
34049                 if(this.labellg > 0){
34050                     labelCls = ' col-lg-' + this.labellg;
34051                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34052                 }
34053
34054                 if(this.labelmd > 0){
34055                     labelCls = ' col-md-' + this.labelmd;
34056                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34057                 }
34058
34059                 if(this.labelsm > 0){
34060                     labelCls = ' col-sm-' + this.labelsm;
34061                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34062                 }
34063
34064                 if(this.labelxs > 0){
34065                     labelCls = ' col-xs-' + this.labelxs;
34066                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34067                 }
34068             }
34069             
34070             label.cls += ' ' + labelCls;
34071             
34072             cfg.cn.push(label);
34073         }
34074         
34075         Roo.each(['day', 'month', 'year'], function(t){
34076             cfg.cn.push({
34077                 tag : 'div',
34078                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34079             });
34080         }, this);
34081         
34082         return cfg;
34083     },
34084     
34085     inputEl: function ()
34086     {
34087         return this.el.select('.roo-date-split-field-group-value', true).first();
34088     },
34089     
34090     onRender : function(ct, position) 
34091     {
34092         var _this = this;
34093         
34094         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34095         
34096         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34097         
34098         this.dayField = new Roo.bootstrap.ComboBox({
34099             allowBlank : this.dayAllowBlank,
34100             alwaysQuery : true,
34101             displayField : 'value',
34102             editable : false,
34103             fieldLabel : '',
34104             forceSelection : true,
34105             mode : 'local',
34106             placeholder : this.dayPlaceholder,
34107             selectOnFocus : true,
34108             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34109             triggerAction : 'all',
34110             typeAhead : true,
34111             valueField : 'value',
34112             store : new Roo.data.SimpleStore({
34113                 data : (function() {    
34114                     var days = [];
34115                     _this.fireEvent('days', _this, days);
34116                     return days;
34117                 })(),
34118                 fields : [ 'value' ]
34119             }),
34120             listeners : {
34121                 select : function (_self, record, index)
34122                 {
34123                     _this.setValue(_this.getValue());
34124                 }
34125             }
34126         });
34127
34128         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34129         
34130         this.monthField = new Roo.bootstrap.MonthField({
34131             after : '<i class=\"fa fa-calendar\"></i>',
34132             allowBlank : this.monthAllowBlank,
34133             placeholder : this.monthPlaceholder,
34134             readOnly : true,
34135             listeners : {
34136                 render : function (_self)
34137                 {
34138                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34139                         e.preventDefault();
34140                         _self.focus();
34141                     });
34142                 },
34143                 select : function (_self, oldvalue, newvalue)
34144                 {
34145                     _this.setValue(_this.getValue());
34146                 }
34147             }
34148         });
34149         
34150         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34151         
34152         this.yearField = new Roo.bootstrap.ComboBox({
34153             allowBlank : this.yearAllowBlank,
34154             alwaysQuery : true,
34155             displayField : 'value',
34156             editable : false,
34157             fieldLabel : '',
34158             forceSelection : true,
34159             mode : 'local',
34160             placeholder : this.yearPlaceholder,
34161             selectOnFocus : true,
34162             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34163             triggerAction : 'all',
34164             typeAhead : true,
34165             valueField : 'value',
34166             store : new Roo.data.SimpleStore({
34167                 data : (function() {
34168                     var years = [];
34169                     _this.fireEvent('years', _this, years);
34170                     return years;
34171                 })(),
34172                 fields : [ 'value' ]
34173             }),
34174             listeners : {
34175                 select : function (_self, record, index)
34176                 {
34177                     _this.setValue(_this.getValue());
34178                 }
34179             }
34180         });
34181
34182         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34183     },
34184     
34185     setValue : function(v, format)
34186     {
34187         this.inputEl.dom.value = v;
34188         
34189         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34190         
34191         var d = Date.parseDate(v, f);
34192         
34193         if(!d){
34194             this.validate();
34195             return;
34196         }
34197         
34198         this.setDay(d.format(this.dayFormat));
34199         this.setMonth(d.format(this.monthFormat));
34200         this.setYear(d.format(this.yearFormat));
34201         
34202         this.validate();
34203         
34204         return;
34205     },
34206     
34207     setDay : function(v)
34208     {
34209         this.dayField.setValue(v);
34210         this.inputEl.dom.value = this.getValue();
34211         this.validate();
34212         return;
34213     },
34214     
34215     setMonth : function(v)
34216     {
34217         this.monthField.setValue(v, true);
34218         this.inputEl.dom.value = this.getValue();
34219         this.validate();
34220         return;
34221     },
34222     
34223     setYear : function(v)
34224     {
34225         this.yearField.setValue(v);
34226         this.inputEl.dom.value = this.getValue();
34227         this.validate();
34228         return;
34229     },
34230     
34231     getDay : function()
34232     {
34233         return this.dayField.getValue();
34234     },
34235     
34236     getMonth : function()
34237     {
34238         return this.monthField.getValue();
34239     },
34240     
34241     getYear : function()
34242     {
34243         return this.yearField.getValue();
34244     },
34245     
34246     getValue : function()
34247     {
34248         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34249         
34250         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34251         
34252         return date;
34253     },
34254     
34255     reset : function()
34256     {
34257         this.setDay('');
34258         this.setMonth('');
34259         this.setYear('');
34260         this.inputEl.dom.value = '';
34261         this.validate();
34262         return;
34263     },
34264     
34265     validate : function()
34266     {
34267         var d = this.dayField.validate();
34268         var m = this.monthField.validate();
34269         var y = this.yearField.validate();
34270         
34271         var valid = true;
34272         
34273         if(
34274                 (!this.dayAllowBlank && !d) ||
34275                 (!this.monthAllowBlank && !m) ||
34276                 (!this.yearAllowBlank && !y)
34277         ){
34278             valid = false;
34279         }
34280         
34281         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34282             return valid;
34283         }
34284         
34285         if(valid){
34286             this.markValid();
34287             return valid;
34288         }
34289         
34290         this.markInvalid();
34291         
34292         return valid;
34293     },
34294     
34295     markValid : function()
34296     {
34297         
34298         var label = this.el.select('label', true).first();
34299         var icon = this.el.select('i.fa-star', true).first();
34300
34301         if(label && icon){
34302             icon.remove();
34303         }
34304         
34305         this.fireEvent('valid', this);
34306     },
34307     
34308      /**
34309      * Mark this field as invalid
34310      * @param {String} msg The validation message
34311      */
34312     markInvalid : function(msg)
34313     {
34314         
34315         var label = this.el.select('label', true).first();
34316         var icon = this.el.select('i.fa-star', true).first();
34317
34318         if(label && !icon){
34319             this.el.select('.roo-date-split-field-label', true).createChild({
34320                 tag : 'i',
34321                 cls : 'text-danger fa fa-lg fa-star',
34322                 tooltip : 'This field is required',
34323                 style : 'margin-right:5px;'
34324             }, label, true);
34325         }
34326         
34327         this.fireEvent('invalid', this, msg);
34328     },
34329     
34330     clearInvalid : function()
34331     {
34332         var label = this.el.select('label', true).first();
34333         var icon = this.el.select('i.fa-star', true).first();
34334
34335         if(label && icon){
34336             icon.remove();
34337         }
34338         
34339         this.fireEvent('valid', this);
34340     },
34341     
34342     getName: function()
34343     {
34344         return this.name;
34345     }
34346     
34347 });
34348
34349  /**
34350  *
34351  * This is based on 
34352  * http://masonry.desandro.com
34353  *
34354  * The idea is to render all the bricks based on vertical width...
34355  *
34356  * The original code extends 'outlayer' - we might need to use that....
34357  * 
34358  */
34359
34360
34361 /**
34362  * @class Roo.bootstrap.LayoutMasonry
34363  * @extends Roo.bootstrap.Component
34364  * Bootstrap Layout Masonry class
34365  * 
34366  * @constructor
34367  * Create a new Element
34368  * @param {Object} config The config object
34369  */
34370
34371 Roo.bootstrap.LayoutMasonry = function(config){
34372     
34373     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34374     
34375     this.bricks = [];
34376     
34377     Roo.bootstrap.LayoutMasonry.register(this);
34378     
34379     this.addEvents({
34380         // raw events
34381         /**
34382          * @event layout
34383          * Fire after layout the items
34384          * @param {Roo.bootstrap.LayoutMasonry} this
34385          * @param {Roo.EventObject} e
34386          */
34387         "layout" : true
34388     });
34389     
34390 };
34391
34392 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34393     
34394     /**
34395      * @cfg {Boolean} isLayoutInstant = no animation?
34396      */   
34397     isLayoutInstant : false, // needed?
34398    
34399     /**
34400      * @cfg {Number} boxWidth  width of the columns
34401      */   
34402     boxWidth : 450,
34403     
34404       /**
34405      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34406      */   
34407     boxHeight : 0,
34408     
34409     /**
34410      * @cfg {Number} padWidth padding below box..
34411      */   
34412     padWidth : 10, 
34413     
34414     /**
34415      * @cfg {Number} gutter gutter width..
34416      */   
34417     gutter : 10,
34418     
34419      /**
34420      * @cfg {Number} maxCols maximum number of columns
34421      */   
34422     
34423     maxCols: 0,
34424     
34425     /**
34426      * @cfg {Boolean} isAutoInitial defalut true
34427      */   
34428     isAutoInitial : true, 
34429     
34430     containerWidth: 0,
34431     
34432     /**
34433      * @cfg {Boolean} isHorizontal defalut false
34434      */   
34435     isHorizontal : false, 
34436
34437     currentSize : null,
34438     
34439     tag: 'div',
34440     
34441     cls: '',
34442     
34443     bricks: null, //CompositeElement
34444     
34445     cols : 1,
34446     
34447     _isLayoutInited : false,
34448     
34449 //    isAlternative : false, // only use for vertical layout...
34450     
34451     /**
34452      * @cfg {Number} alternativePadWidth padding below box..
34453      */   
34454     alternativePadWidth : 50,
34455     
34456     selectedBrick : [],
34457     
34458     getAutoCreate : function(){
34459         
34460         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34461         
34462         var cfg = {
34463             tag: this.tag,
34464             cls: 'blog-masonary-wrapper ' + this.cls,
34465             cn : {
34466                 cls : 'mas-boxes masonary'
34467             }
34468         };
34469         
34470         return cfg;
34471     },
34472     
34473     getChildContainer: function( )
34474     {
34475         if (this.boxesEl) {
34476             return this.boxesEl;
34477         }
34478         
34479         this.boxesEl = this.el.select('.mas-boxes').first();
34480         
34481         return this.boxesEl;
34482     },
34483     
34484     
34485     initEvents : function()
34486     {
34487         var _this = this;
34488         
34489         if(this.isAutoInitial){
34490             Roo.log('hook children rendered');
34491             this.on('childrenrendered', function() {
34492                 Roo.log('children rendered');
34493                 _this.initial();
34494             } ,this);
34495         }
34496     },
34497     
34498     initial : function()
34499     {
34500         this.selectedBrick = [];
34501         
34502         this.currentSize = this.el.getBox(true);
34503         
34504         Roo.EventManager.onWindowResize(this.resize, this); 
34505
34506         if(!this.isAutoInitial){
34507             this.layout();
34508             return;
34509         }
34510         
34511         this.layout();
34512         
34513         return;
34514         //this.layout.defer(500,this);
34515         
34516     },
34517     
34518     resize : function()
34519     {
34520         var cs = this.el.getBox(true);
34521         
34522         if (
34523                 this.currentSize.width == cs.width && 
34524                 this.currentSize.x == cs.x && 
34525                 this.currentSize.height == cs.height && 
34526                 this.currentSize.y == cs.y 
34527         ) {
34528             Roo.log("no change in with or X or Y");
34529             return;
34530         }
34531         
34532         this.currentSize = cs;
34533         
34534         this.layout();
34535         
34536     },
34537     
34538     layout : function()
34539     {   
34540         this._resetLayout();
34541         
34542         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34543         
34544         this.layoutItems( isInstant );
34545       
34546         this._isLayoutInited = true;
34547         
34548         this.fireEvent('layout', this);
34549         
34550     },
34551     
34552     _resetLayout : function()
34553     {
34554         if(this.isHorizontal){
34555             this.horizontalMeasureColumns();
34556             return;
34557         }
34558         
34559         this.verticalMeasureColumns();
34560         
34561     },
34562     
34563     verticalMeasureColumns : function()
34564     {
34565         this.getContainerWidth();
34566         
34567 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34568 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34569 //            return;
34570 //        }
34571         
34572         var boxWidth = this.boxWidth + this.padWidth;
34573         
34574         if(this.containerWidth < this.boxWidth){
34575             boxWidth = this.containerWidth
34576         }
34577         
34578         var containerWidth = this.containerWidth;
34579         
34580         var cols = Math.floor(containerWidth / boxWidth);
34581         
34582         this.cols = Math.max( cols, 1 );
34583         
34584         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34585         
34586         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34587         
34588         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34589         
34590         this.colWidth = boxWidth + avail - this.padWidth;
34591         
34592         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34593         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34594     },
34595     
34596     horizontalMeasureColumns : function()
34597     {
34598         this.getContainerWidth();
34599         
34600         var boxWidth = this.boxWidth;
34601         
34602         if(this.containerWidth < boxWidth){
34603             boxWidth = this.containerWidth;
34604         }
34605         
34606         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34607         
34608         this.el.setHeight(boxWidth);
34609         
34610     },
34611     
34612     getContainerWidth : function()
34613     {
34614         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34615     },
34616     
34617     layoutItems : function( isInstant )
34618     {
34619         Roo.log(this.bricks);
34620         
34621         var items = Roo.apply([], this.bricks);
34622         
34623         if(this.isHorizontal){
34624             this._horizontalLayoutItems( items , isInstant );
34625             return;
34626         }
34627         
34628 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34629 //            this._verticalAlternativeLayoutItems( items , isInstant );
34630 //            return;
34631 //        }
34632         
34633         this._verticalLayoutItems( items , isInstant );
34634         
34635     },
34636     
34637     _verticalLayoutItems : function ( items , isInstant)
34638     {
34639         if ( !items || !items.length ) {
34640             return;
34641         }
34642         
34643         var standard = [
34644             ['xs', 'xs', 'xs', 'tall'],
34645             ['xs', 'xs', 'tall'],
34646             ['xs', 'xs', 'sm'],
34647             ['xs', 'xs', 'xs'],
34648             ['xs', 'tall'],
34649             ['xs', 'sm'],
34650             ['xs', 'xs'],
34651             ['xs'],
34652             
34653             ['sm', 'xs', 'xs'],
34654             ['sm', 'xs'],
34655             ['sm'],
34656             
34657             ['tall', 'xs', 'xs', 'xs'],
34658             ['tall', 'xs', 'xs'],
34659             ['tall', 'xs'],
34660             ['tall']
34661             
34662         ];
34663         
34664         var queue = [];
34665         
34666         var boxes = [];
34667         
34668         var box = [];
34669         
34670         Roo.each(items, function(item, k){
34671             
34672             switch (item.size) {
34673                 // these layouts take up a full box,
34674                 case 'md' :
34675                 case 'md-left' :
34676                 case 'md-right' :
34677                 case 'wide' :
34678                     
34679                     if(box.length){
34680                         boxes.push(box);
34681                         box = [];
34682                     }
34683                     
34684                     boxes.push([item]);
34685                     
34686                     break;
34687                     
34688                 case 'xs' :
34689                 case 'sm' :
34690                 case 'tall' :
34691                     
34692                     box.push(item);
34693                     
34694                     break;
34695                 default :
34696                     break;
34697                     
34698             }
34699             
34700         }, this);
34701         
34702         if(box.length){
34703             boxes.push(box);
34704             box = [];
34705         }
34706         
34707         var filterPattern = function(box, length)
34708         {
34709             if(!box.length){
34710                 return;
34711             }
34712             
34713             var match = false;
34714             
34715             var pattern = box.slice(0, length);
34716             
34717             var format = [];
34718             
34719             Roo.each(pattern, function(i){
34720                 format.push(i.size);
34721             }, this);
34722             
34723             Roo.each(standard, function(s){
34724                 
34725                 if(String(s) != String(format)){
34726                     return;
34727                 }
34728                 
34729                 match = true;
34730                 return false;
34731                 
34732             }, this);
34733             
34734             if(!match && length == 1){
34735                 return;
34736             }
34737             
34738             if(!match){
34739                 filterPattern(box, length - 1);
34740                 return;
34741             }
34742                 
34743             queue.push(pattern);
34744
34745             box = box.slice(length, box.length);
34746
34747             filterPattern(box, 4);
34748
34749             return;
34750             
34751         }
34752         
34753         Roo.each(boxes, function(box, k){
34754             
34755             if(!box.length){
34756                 return;
34757             }
34758             
34759             if(box.length == 1){
34760                 queue.push(box);
34761                 return;
34762             }
34763             
34764             filterPattern(box, 4);
34765             
34766         }, this);
34767         
34768         this._processVerticalLayoutQueue( queue, isInstant );
34769         
34770     },
34771     
34772 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34773 //    {
34774 //        if ( !items || !items.length ) {
34775 //            return;
34776 //        }
34777 //
34778 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34779 //        
34780 //    },
34781     
34782     _horizontalLayoutItems : function ( items , isInstant)
34783     {
34784         if ( !items || !items.length || items.length < 3) {
34785             return;
34786         }
34787         
34788         items.reverse();
34789         
34790         var eItems = items.slice(0, 3);
34791         
34792         items = items.slice(3, items.length);
34793         
34794         var standard = [
34795             ['xs', 'xs', 'xs', 'wide'],
34796             ['xs', 'xs', 'wide'],
34797             ['xs', 'xs', 'sm'],
34798             ['xs', 'xs', 'xs'],
34799             ['xs', 'wide'],
34800             ['xs', 'sm'],
34801             ['xs', 'xs'],
34802             ['xs'],
34803             
34804             ['sm', 'xs', 'xs'],
34805             ['sm', 'xs'],
34806             ['sm'],
34807             
34808             ['wide', 'xs', 'xs', 'xs'],
34809             ['wide', 'xs', 'xs'],
34810             ['wide', 'xs'],
34811             ['wide'],
34812             
34813             ['wide-thin']
34814         ];
34815         
34816         var queue = [];
34817         
34818         var boxes = [];
34819         
34820         var box = [];
34821         
34822         Roo.each(items, function(item, k){
34823             
34824             switch (item.size) {
34825                 case 'md' :
34826                 case 'md-left' :
34827                 case 'md-right' :
34828                 case 'tall' :
34829                     
34830                     if(box.length){
34831                         boxes.push(box);
34832                         box = [];
34833                     }
34834                     
34835                     boxes.push([item]);
34836                     
34837                     break;
34838                     
34839                 case 'xs' :
34840                 case 'sm' :
34841                 case 'wide' :
34842                 case 'wide-thin' :
34843                     
34844                     box.push(item);
34845                     
34846                     break;
34847                 default :
34848                     break;
34849                     
34850             }
34851             
34852         }, this);
34853         
34854         if(box.length){
34855             boxes.push(box);
34856             box = [];
34857         }
34858         
34859         var filterPattern = function(box, length)
34860         {
34861             if(!box.length){
34862                 return;
34863             }
34864             
34865             var match = false;
34866             
34867             var pattern = box.slice(0, length);
34868             
34869             var format = [];
34870             
34871             Roo.each(pattern, function(i){
34872                 format.push(i.size);
34873             }, this);
34874             
34875             Roo.each(standard, function(s){
34876                 
34877                 if(String(s) != String(format)){
34878                     return;
34879                 }
34880                 
34881                 match = true;
34882                 return false;
34883                 
34884             }, this);
34885             
34886             if(!match && length == 1){
34887                 return;
34888             }
34889             
34890             if(!match){
34891                 filterPattern(box, length - 1);
34892                 return;
34893             }
34894                 
34895             queue.push(pattern);
34896
34897             box = box.slice(length, box.length);
34898
34899             filterPattern(box, 4);
34900
34901             return;
34902             
34903         }
34904         
34905         Roo.each(boxes, function(box, k){
34906             
34907             if(!box.length){
34908                 return;
34909             }
34910             
34911             if(box.length == 1){
34912                 queue.push(box);
34913                 return;
34914             }
34915             
34916             filterPattern(box, 4);
34917             
34918         }, this);
34919         
34920         
34921         var prune = [];
34922         
34923         var pos = this.el.getBox(true);
34924         
34925         var minX = pos.x;
34926         
34927         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34928         
34929         var hit_end = false;
34930         
34931         Roo.each(queue, function(box){
34932             
34933             if(hit_end){
34934                 
34935                 Roo.each(box, function(b){
34936                 
34937                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34938                     b.el.hide();
34939
34940                 }, this);
34941
34942                 return;
34943             }
34944             
34945             var mx = 0;
34946             
34947             Roo.each(box, function(b){
34948                 
34949                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34950                 b.el.show();
34951
34952                 mx = Math.max(mx, b.x);
34953                 
34954             }, this);
34955             
34956             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34957             
34958             if(maxX < minX){
34959                 
34960                 Roo.each(box, function(b){
34961                 
34962                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34963                     b.el.hide();
34964                     
34965                 }, this);
34966                 
34967                 hit_end = true;
34968                 
34969                 return;
34970             }
34971             
34972             prune.push(box);
34973             
34974         }, this);
34975         
34976         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34977     },
34978     
34979     /** Sets position of item in DOM
34980     * @param {Element} item
34981     * @param {Number} x - horizontal position
34982     * @param {Number} y - vertical position
34983     * @param {Boolean} isInstant - disables transitions
34984     */
34985     _processVerticalLayoutQueue : function( queue, isInstant )
34986     {
34987         var pos = this.el.getBox(true);
34988         var x = pos.x;
34989         var y = pos.y;
34990         var maxY = [];
34991         
34992         for (var i = 0; i < this.cols; i++){
34993             maxY[i] = pos.y;
34994         }
34995         
34996         Roo.each(queue, function(box, k){
34997             
34998             var col = k % this.cols;
34999             
35000             Roo.each(box, function(b,kk){
35001                 
35002                 b.el.position('absolute');
35003                 
35004                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35005                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35006                 
35007                 if(b.size == 'md-left' || b.size == 'md-right'){
35008                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35009                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35010                 }
35011                 
35012                 b.el.setWidth(width);
35013                 b.el.setHeight(height);
35014                 // iframe?
35015                 b.el.select('iframe',true).setSize(width,height);
35016                 
35017             }, this);
35018             
35019             for (var i = 0; i < this.cols; i++){
35020                 
35021                 if(maxY[i] < maxY[col]){
35022                     col = i;
35023                     continue;
35024                 }
35025                 
35026                 col = Math.min(col, i);
35027                 
35028             }
35029             
35030             x = pos.x + col * (this.colWidth + this.padWidth);
35031             
35032             y = maxY[col];
35033             
35034             var positions = [];
35035             
35036             switch (box.length){
35037                 case 1 :
35038                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35039                     break;
35040                 case 2 :
35041                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35042                     break;
35043                 case 3 :
35044                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35045                     break;
35046                 case 4 :
35047                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35048                     break;
35049                 default :
35050                     break;
35051             }
35052             
35053             Roo.each(box, function(b,kk){
35054                 
35055                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35056                 
35057                 var sz = b.el.getSize();
35058                 
35059                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35060                 
35061             }, this);
35062             
35063         }, this);
35064         
35065         var mY = 0;
35066         
35067         for (var i = 0; i < this.cols; i++){
35068             mY = Math.max(mY, maxY[i]);
35069         }
35070         
35071         this.el.setHeight(mY - pos.y);
35072         
35073     },
35074     
35075 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35076 //    {
35077 //        var pos = this.el.getBox(true);
35078 //        var x = pos.x;
35079 //        var y = pos.y;
35080 //        var maxX = pos.right;
35081 //        
35082 //        var maxHeight = 0;
35083 //        
35084 //        Roo.each(items, function(item, k){
35085 //            
35086 //            var c = k % 2;
35087 //            
35088 //            item.el.position('absolute');
35089 //                
35090 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35091 //
35092 //            item.el.setWidth(width);
35093 //
35094 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35095 //
35096 //            item.el.setHeight(height);
35097 //            
35098 //            if(c == 0){
35099 //                item.el.setXY([x, y], isInstant ? false : true);
35100 //            } else {
35101 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35102 //            }
35103 //            
35104 //            y = y + height + this.alternativePadWidth;
35105 //            
35106 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35107 //            
35108 //        }, this);
35109 //        
35110 //        this.el.setHeight(maxHeight);
35111 //        
35112 //    },
35113     
35114     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35115     {
35116         var pos = this.el.getBox(true);
35117         
35118         var minX = pos.x;
35119         var minY = pos.y;
35120         
35121         var maxX = pos.right;
35122         
35123         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35124         
35125         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35126         
35127         Roo.each(queue, function(box, k){
35128             
35129             Roo.each(box, function(b, kk){
35130                 
35131                 b.el.position('absolute');
35132                 
35133                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35134                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35135                 
35136                 if(b.size == 'md-left' || b.size == 'md-right'){
35137                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35138                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35139                 }
35140                 
35141                 b.el.setWidth(width);
35142                 b.el.setHeight(height);
35143                 
35144             }, this);
35145             
35146             if(!box.length){
35147                 return;
35148             }
35149             
35150             var positions = [];
35151             
35152             switch (box.length){
35153                 case 1 :
35154                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35155                     break;
35156                 case 2 :
35157                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35158                     break;
35159                 case 3 :
35160                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35161                     break;
35162                 case 4 :
35163                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35164                     break;
35165                 default :
35166                     break;
35167             }
35168             
35169             Roo.each(box, function(b,kk){
35170                 
35171                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35172                 
35173                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35174                 
35175             }, this);
35176             
35177         }, this);
35178         
35179     },
35180     
35181     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35182     {
35183         Roo.each(eItems, function(b,k){
35184             
35185             b.size = (k == 0) ? 'sm' : 'xs';
35186             b.x = (k == 0) ? 2 : 1;
35187             b.y = (k == 0) ? 2 : 1;
35188             
35189             b.el.position('absolute');
35190             
35191             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35192                 
35193             b.el.setWidth(width);
35194             
35195             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35196             
35197             b.el.setHeight(height);
35198             
35199         }, this);
35200
35201         var positions = [];
35202         
35203         positions.push({
35204             x : maxX - this.unitWidth * 2 - this.gutter,
35205             y : minY
35206         });
35207         
35208         positions.push({
35209             x : maxX - this.unitWidth,
35210             y : minY + (this.unitWidth + this.gutter) * 2
35211         });
35212         
35213         positions.push({
35214             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35215             y : minY
35216         });
35217         
35218         Roo.each(eItems, function(b,k){
35219             
35220             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35221
35222         }, this);
35223         
35224     },
35225     
35226     getVerticalOneBoxColPositions : function(x, y, box)
35227     {
35228         var pos = [];
35229         
35230         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35231         
35232         if(box[0].size == 'md-left'){
35233             rand = 0;
35234         }
35235         
35236         if(box[0].size == 'md-right'){
35237             rand = 1;
35238         }
35239         
35240         pos.push({
35241             x : x + (this.unitWidth + this.gutter) * rand,
35242             y : y
35243         });
35244         
35245         return pos;
35246     },
35247     
35248     getVerticalTwoBoxColPositions : function(x, y, box)
35249     {
35250         var pos = [];
35251         
35252         if(box[0].size == 'xs'){
35253             
35254             pos.push({
35255                 x : x,
35256                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35257             });
35258
35259             pos.push({
35260                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35261                 y : y
35262             });
35263             
35264             return pos;
35265             
35266         }
35267         
35268         pos.push({
35269             x : x,
35270             y : y
35271         });
35272
35273         pos.push({
35274             x : x + (this.unitWidth + this.gutter) * 2,
35275             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35276         });
35277         
35278         return pos;
35279         
35280     },
35281     
35282     getVerticalThreeBoxColPositions : function(x, y, box)
35283     {
35284         var pos = [];
35285         
35286         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35287             
35288             pos.push({
35289                 x : x,
35290                 y : y
35291             });
35292
35293             pos.push({
35294                 x : x + (this.unitWidth + this.gutter) * 1,
35295                 y : y
35296             });
35297             
35298             pos.push({
35299                 x : x + (this.unitWidth + this.gutter) * 2,
35300                 y : y
35301             });
35302             
35303             return pos;
35304             
35305         }
35306         
35307         if(box[0].size == 'xs' && box[1].size == 'xs'){
35308             
35309             pos.push({
35310                 x : x,
35311                 y : y
35312             });
35313
35314             pos.push({
35315                 x : x,
35316                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35317             });
35318             
35319             pos.push({
35320                 x : x + (this.unitWidth + this.gutter) * 1,
35321                 y : y
35322             });
35323             
35324             return pos;
35325             
35326         }
35327         
35328         pos.push({
35329             x : x,
35330             y : y
35331         });
35332
35333         pos.push({
35334             x : x + (this.unitWidth + this.gutter) * 2,
35335             y : y
35336         });
35337
35338         pos.push({
35339             x : x + (this.unitWidth + this.gutter) * 2,
35340             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35341         });
35342             
35343         return pos;
35344         
35345     },
35346     
35347     getVerticalFourBoxColPositions : function(x, y, box)
35348     {
35349         var pos = [];
35350         
35351         if(box[0].size == 'xs'){
35352             
35353             pos.push({
35354                 x : x,
35355                 y : y
35356             });
35357
35358             pos.push({
35359                 x : x,
35360                 y : y + (this.unitHeight + this.gutter) * 1
35361             });
35362             
35363             pos.push({
35364                 x : x,
35365                 y : y + (this.unitHeight + this.gutter) * 2
35366             });
35367             
35368             pos.push({
35369                 x : x + (this.unitWidth + this.gutter) * 1,
35370                 y : y
35371             });
35372             
35373             return pos;
35374             
35375         }
35376         
35377         pos.push({
35378             x : x,
35379             y : y
35380         });
35381
35382         pos.push({
35383             x : x + (this.unitWidth + this.gutter) * 2,
35384             y : y
35385         });
35386
35387         pos.push({
35388             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35389             y : y + (this.unitHeight + this.gutter) * 1
35390         });
35391
35392         pos.push({
35393             x : x + (this.unitWidth + this.gutter) * 2,
35394             y : y + (this.unitWidth + this.gutter) * 2
35395         });
35396
35397         return pos;
35398         
35399     },
35400     
35401     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35402     {
35403         var pos = [];
35404         
35405         if(box[0].size == 'md-left'){
35406             pos.push({
35407                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35408                 y : minY
35409             });
35410             
35411             return pos;
35412         }
35413         
35414         if(box[0].size == 'md-right'){
35415             pos.push({
35416                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35417                 y : minY + (this.unitWidth + this.gutter) * 1
35418             });
35419             
35420             return pos;
35421         }
35422         
35423         var rand = Math.floor(Math.random() * (4 - box[0].y));
35424         
35425         pos.push({
35426             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35427             y : minY + (this.unitWidth + this.gutter) * rand
35428         });
35429         
35430         return pos;
35431         
35432     },
35433     
35434     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35435     {
35436         var pos = [];
35437         
35438         if(box[0].size == 'xs'){
35439             
35440             pos.push({
35441                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35442                 y : minY
35443             });
35444
35445             pos.push({
35446                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35447                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35448             });
35449             
35450             return pos;
35451             
35452         }
35453         
35454         pos.push({
35455             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35456             y : minY
35457         });
35458
35459         pos.push({
35460             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35461             y : minY + (this.unitWidth + this.gutter) * 2
35462         });
35463         
35464         return pos;
35465         
35466     },
35467     
35468     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35469     {
35470         var pos = [];
35471         
35472         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35473             
35474             pos.push({
35475                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35476                 y : minY
35477             });
35478
35479             pos.push({
35480                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35481                 y : minY + (this.unitWidth + this.gutter) * 1
35482             });
35483             
35484             pos.push({
35485                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35486                 y : minY + (this.unitWidth + this.gutter) * 2
35487             });
35488             
35489             return pos;
35490             
35491         }
35492         
35493         if(box[0].size == 'xs' && box[1].size == 'xs'){
35494             
35495             pos.push({
35496                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35497                 y : minY
35498             });
35499
35500             pos.push({
35501                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35502                 y : minY
35503             });
35504             
35505             pos.push({
35506                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35507                 y : minY + (this.unitWidth + this.gutter) * 1
35508             });
35509             
35510             return pos;
35511             
35512         }
35513         
35514         pos.push({
35515             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35516             y : minY
35517         });
35518
35519         pos.push({
35520             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35521             y : minY + (this.unitWidth + this.gutter) * 2
35522         });
35523
35524         pos.push({
35525             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35526             y : minY + (this.unitWidth + this.gutter) * 2
35527         });
35528             
35529         return pos;
35530         
35531     },
35532     
35533     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35534     {
35535         var pos = [];
35536         
35537         if(box[0].size == 'xs'){
35538             
35539             pos.push({
35540                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35541                 y : minY
35542             });
35543
35544             pos.push({
35545                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35546                 y : minY
35547             });
35548             
35549             pos.push({
35550                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35551                 y : minY
35552             });
35553             
35554             pos.push({
35555                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35556                 y : minY + (this.unitWidth + this.gutter) * 1
35557             });
35558             
35559             return pos;
35560             
35561         }
35562         
35563         pos.push({
35564             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35565             y : minY
35566         });
35567         
35568         pos.push({
35569             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35570             y : minY + (this.unitWidth + this.gutter) * 2
35571         });
35572         
35573         pos.push({
35574             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35575             y : minY + (this.unitWidth + this.gutter) * 2
35576         });
35577         
35578         pos.push({
35579             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35580             y : minY + (this.unitWidth + this.gutter) * 2
35581         });
35582
35583         return pos;
35584         
35585     },
35586     
35587     /**
35588     * remove a Masonry Brick
35589     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35590     */
35591     removeBrick : function(brick_id)
35592     {
35593         if (!brick_id) {
35594             return;
35595         }
35596         
35597         for (var i = 0; i<this.bricks.length; i++) {
35598             if (this.bricks[i].id == brick_id) {
35599                 this.bricks.splice(i,1);
35600                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35601                 this.initial();
35602             }
35603         }
35604     },
35605     
35606     /**
35607     * adds a Masonry Brick
35608     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35609     */
35610     addBrick : function(cfg)
35611     {
35612         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35613         //this.register(cn);
35614         cn.parentId = this.id;
35615         cn.render(this.el);
35616         return cn;
35617     },
35618     
35619     /**
35620     * register a Masonry Brick
35621     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35622     */
35623     
35624     register : function(brick)
35625     {
35626         this.bricks.push(brick);
35627         brick.masonryId = this.id;
35628     },
35629     
35630     /**
35631     * clear all the Masonry Brick
35632     */
35633     clearAll : function()
35634     {
35635         this.bricks = [];
35636         //this.getChildContainer().dom.innerHTML = "";
35637         this.el.dom.innerHTML = '';
35638     },
35639     
35640     getSelected : function()
35641     {
35642         if (!this.selectedBrick) {
35643             return false;
35644         }
35645         
35646         return this.selectedBrick;
35647     }
35648 });
35649
35650 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35651     
35652     groups: {},
35653      /**
35654     * register a Masonry Layout
35655     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35656     */
35657     
35658     register : function(layout)
35659     {
35660         this.groups[layout.id] = layout;
35661     },
35662     /**
35663     * fetch a  Masonry Layout based on the masonry layout ID
35664     * @param {string} the masonry layout to add
35665     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35666     */
35667     
35668     get: function(layout_id) {
35669         if (typeof(this.groups[layout_id]) == 'undefined') {
35670             return false;
35671         }
35672         return this.groups[layout_id] ;
35673     }
35674     
35675     
35676     
35677 });
35678
35679  
35680
35681  /**
35682  *
35683  * This is based on 
35684  * http://masonry.desandro.com
35685  *
35686  * The idea is to render all the bricks based on vertical width...
35687  *
35688  * The original code extends 'outlayer' - we might need to use that....
35689  * 
35690  */
35691
35692
35693 /**
35694  * @class Roo.bootstrap.LayoutMasonryAuto
35695  * @extends Roo.bootstrap.Component
35696  * Bootstrap Layout Masonry class
35697  * 
35698  * @constructor
35699  * Create a new Element
35700  * @param {Object} config The config object
35701  */
35702
35703 Roo.bootstrap.LayoutMasonryAuto = function(config){
35704     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35705 };
35706
35707 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35708     
35709       /**
35710      * @cfg {Boolean} isFitWidth  - resize the width..
35711      */   
35712     isFitWidth : false,  // options..
35713     /**
35714      * @cfg {Boolean} isOriginLeft = left align?
35715      */   
35716     isOriginLeft : true,
35717     /**
35718      * @cfg {Boolean} isOriginTop = top align?
35719      */   
35720     isOriginTop : false,
35721     /**
35722      * @cfg {Boolean} isLayoutInstant = no animation?
35723      */   
35724     isLayoutInstant : false, // needed?
35725     /**
35726      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35727      */   
35728     isResizingContainer : true,
35729     /**
35730      * @cfg {Number} columnWidth  width of the columns 
35731      */   
35732     
35733     columnWidth : 0,
35734     
35735     /**
35736      * @cfg {Number} maxCols maximum number of columns
35737      */   
35738     
35739     maxCols: 0,
35740     /**
35741      * @cfg {Number} padHeight padding below box..
35742      */   
35743     
35744     padHeight : 10, 
35745     
35746     /**
35747      * @cfg {Boolean} isAutoInitial defalut true
35748      */   
35749     
35750     isAutoInitial : true, 
35751     
35752     // private?
35753     gutter : 0,
35754     
35755     containerWidth: 0,
35756     initialColumnWidth : 0,
35757     currentSize : null,
35758     
35759     colYs : null, // array.
35760     maxY : 0,
35761     padWidth: 10,
35762     
35763     
35764     tag: 'div',
35765     cls: '',
35766     bricks: null, //CompositeElement
35767     cols : 0, // array?
35768     // element : null, // wrapped now this.el
35769     _isLayoutInited : null, 
35770     
35771     
35772     getAutoCreate : function(){
35773         
35774         var cfg = {
35775             tag: this.tag,
35776             cls: 'blog-masonary-wrapper ' + this.cls,
35777             cn : {
35778                 cls : 'mas-boxes masonary'
35779             }
35780         };
35781         
35782         return cfg;
35783     },
35784     
35785     getChildContainer: function( )
35786     {
35787         if (this.boxesEl) {
35788             return this.boxesEl;
35789         }
35790         
35791         this.boxesEl = this.el.select('.mas-boxes').first();
35792         
35793         return this.boxesEl;
35794     },
35795     
35796     
35797     initEvents : function()
35798     {
35799         var _this = this;
35800         
35801         if(this.isAutoInitial){
35802             Roo.log('hook children rendered');
35803             this.on('childrenrendered', function() {
35804                 Roo.log('children rendered');
35805                 _this.initial();
35806             } ,this);
35807         }
35808         
35809     },
35810     
35811     initial : function()
35812     {
35813         this.reloadItems();
35814
35815         this.currentSize = this.el.getBox(true);
35816
35817         /// was window resize... - let's see if this works..
35818         Roo.EventManager.onWindowResize(this.resize, this); 
35819
35820         if(!this.isAutoInitial){
35821             this.layout();
35822             return;
35823         }
35824         
35825         this.layout.defer(500,this);
35826     },
35827     
35828     reloadItems: function()
35829     {
35830         this.bricks = this.el.select('.masonry-brick', true);
35831         
35832         this.bricks.each(function(b) {
35833             //Roo.log(b.getSize());
35834             if (!b.attr('originalwidth')) {
35835                 b.attr('originalwidth',  b.getSize().width);
35836             }
35837             
35838         });
35839         
35840         Roo.log(this.bricks.elements.length);
35841     },
35842     
35843     resize : function()
35844     {
35845         Roo.log('resize');
35846         var cs = this.el.getBox(true);
35847         
35848         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35849             Roo.log("no change in with or X");
35850             return;
35851         }
35852         this.currentSize = cs;
35853         this.layout();
35854     },
35855     
35856     layout : function()
35857     {
35858          Roo.log('layout');
35859         this._resetLayout();
35860         //this._manageStamps();
35861       
35862         // don't animate first layout
35863         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35864         this.layoutItems( isInstant );
35865       
35866         // flag for initalized
35867         this._isLayoutInited = true;
35868     },
35869     
35870     layoutItems : function( isInstant )
35871     {
35872         //var items = this._getItemsForLayout( this.items );
35873         // original code supports filtering layout items.. we just ignore it..
35874         
35875         this._layoutItems( this.bricks , isInstant );
35876       
35877         this._postLayout();
35878     },
35879     _layoutItems : function ( items , isInstant)
35880     {
35881        //this.fireEvent( 'layout', this, items );
35882     
35883
35884         if ( !items || !items.elements.length ) {
35885           // no items, emit event with empty array
35886             return;
35887         }
35888
35889         var queue = [];
35890         items.each(function(item) {
35891             Roo.log("layout item");
35892             Roo.log(item);
35893             // get x/y object from method
35894             var position = this._getItemLayoutPosition( item );
35895             // enqueue
35896             position.item = item;
35897             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35898             queue.push( position );
35899         }, this);
35900       
35901         this._processLayoutQueue( queue );
35902     },
35903     /** Sets position of item in DOM
35904     * @param {Element} item
35905     * @param {Number} x - horizontal position
35906     * @param {Number} y - vertical position
35907     * @param {Boolean} isInstant - disables transitions
35908     */
35909     _processLayoutQueue : function( queue )
35910     {
35911         for ( var i=0, len = queue.length; i < len; i++ ) {
35912             var obj = queue[i];
35913             obj.item.position('absolute');
35914             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35915         }
35916     },
35917       
35918     
35919     /**
35920     * Any logic you want to do after each layout,
35921     * i.e. size the container
35922     */
35923     _postLayout : function()
35924     {
35925         this.resizeContainer();
35926     },
35927     
35928     resizeContainer : function()
35929     {
35930         if ( !this.isResizingContainer ) {
35931             return;
35932         }
35933         var size = this._getContainerSize();
35934         if ( size ) {
35935             this.el.setSize(size.width,size.height);
35936             this.boxesEl.setSize(size.width,size.height);
35937         }
35938     },
35939     
35940     
35941     
35942     _resetLayout : function()
35943     {
35944         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35945         this.colWidth = this.el.getWidth();
35946         //this.gutter = this.el.getWidth(); 
35947         
35948         this.measureColumns();
35949
35950         // reset column Y
35951         var i = this.cols;
35952         this.colYs = [];
35953         while (i--) {
35954             this.colYs.push( 0 );
35955         }
35956     
35957         this.maxY = 0;
35958     },
35959
35960     measureColumns : function()
35961     {
35962         this.getContainerWidth();
35963       // if columnWidth is 0, default to outerWidth of first item
35964         if ( !this.columnWidth ) {
35965             var firstItem = this.bricks.first();
35966             Roo.log(firstItem);
35967             this.columnWidth  = this.containerWidth;
35968             if (firstItem && firstItem.attr('originalwidth') ) {
35969                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35970             }
35971             // columnWidth fall back to item of first element
35972             Roo.log("set column width?");
35973                         this.initialColumnWidth = this.columnWidth  ;
35974
35975             // if first elem has no width, default to size of container
35976             
35977         }
35978         
35979         
35980         if (this.initialColumnWidth) {
35981             this.columnWidth = this.initialColumnWidth;
35982         }
35983         
35984         
35985             
35986         // column width is fixed at the top - however if container width get's smaller we should
35987         // reduce it...
35988         
35989         // this bit calcs how man columns..
35990             
35991         var columnWidth = this.columnWidth += this.gutter;
35992       
35993         // calculate columns
35994         var containerWidth = this.containerWidth + this.gutter;
35995         
35996         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35997         // fix rounding errors, typically with gutters
35998         var excess = columnWidth - containerWidth % columnWidth;
35999         
36000         
36001         // if overshoot is less than a pixel, round up, otherwise floor it
36002         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
36003         cols = Math[ mathMethod ]( cols );
36004         this.cols = Math.max( cols, 1 );
36005         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36006         
36007          // padding positioning..
36008         var totalColWidth = this.cols * this.columnWidth;
36009         var padavail = this.containerWidth - totalColWidth;
36010         // so for 2 columns - we need 3 'pads'
36011         
36012         var padNeeded = (1+this.cols) * this.padWidth;
36013         
36014         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36015         
36016         this.columnWidth += padExtra
36017         //this.padWidth = Math.floor(padavail /  ( this.cols));
36018         
36019         // adjust colum width so that padding is fixed??
36020         
36021         // we have 3 columns ... total = width * 3
36022         // we have X left over... that should be used by 
36023         
36024         //if (this.expandC) {
36025             
36026         //}
36027         
36028         
36029         
36030     },
36031     
36032     getContainerWidth : function()
36033     {
36034        /* // container is parent if fit width
36035         var container = this.isFitWidth ? this.element.parentNode : this.element;
36036         // check that this.size and size are there
36037         // IE8 triggers resize on body size change, so they might not be
36038         
36039         var size = getSize( container );  //FIXME
36040         this.containerWidth = size && size.innerWidth; //FIXME
36041         */
36042          
36043         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36044         
36045     },
36046     
36047     _getItemLayoutPosition : function( item )  // what is item?
36048     {
36049         // we resize the item to our columnWidth..
36050       
36051         item.setWidth(this.columnWidth);
36052         item.autoBoxAdjust  = false;
36053         
36054         var sz = item.getSize();
36055  
36056         // how many columns does this brick span
36057         var remainder = this.containerWidth % this.columnWidth;
36058         
36059         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36060         // round if off by 1 pixel, otherwise use ceil
36061         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36062         colSpan = Math.min( colSpan, this.cols );
36063         
36064         // normally this should be '1' as we dont' currently allow multi width columns..
36065         
36066         var colGroup = this._getColGroup( colSpan );
36067         // get the minimum Y value from the columns
36068         var minimumY = Math.min.apply( Math, colGroup );
36069         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36070         
36071         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36072          
36073         // position the brick
36074         var position = {
36075             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36076             y: this.currentSize.y + minimumY + this.padHeight
36077         };
36078         
36079         Roo.log(position);
36080         // apply setHeight to necessary columns
36081         var setHeight = minimumY + sz.height + this.padHeight;
36082         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36083         
36084         var setSpan = this.cols + 1 - colGroup.length;
36085         for ( var i = 0; i < setSpan; i++ ) {
36086           this.colYs[ shortColIndex + i ] = setHeight ;
36087         }
36088       
36089         return position;
36090     },
36091     
36092     /**
36093      * @param {Number} colSpan - number of columns the element spans
36094      * @returns {Array} colGroup
36095      */
36096     _getColGroup : function( colSpan )
36097     {
36098         if ( colSpan < 2 ) {
36099           // if brick spans only one column, use all the column Ys
36100           return this.colYs;
36101         }
36102       
36103         var colGroup = [];
36104         // how many different places could this brick fit horizontally
36105         var groupCount = this.cols + 1 - colSpan;
36106         // for each group potential horizontal position
36107         for ( var i = 0; i < groupCount; i++ ) {
36108           // make an array of colY values for that one group
36109           var groupColYs = this.colYs.slice( i, i + colSpan );
36110           // and get the max value of the array
36111           colGroup[i] = Math.max.apply( Math, groupColYs );
36112         }
36113         return colGroup;
36114     },
36115     /*
36116     _manageStamp : function( stamp )
36117     {
36118         var stampSize =  stamp.getSize();
36119         var offset = stamp.getBox();
36120         // get the columns that this stamp affects
36121         var firstX = this.isOriginLeft ? offset.x : offset.right;
36122         var lastX = firstX + stampSize.width;
36123         var firstCol = Math.floor( firstX / this.columnWidth );
36124         firstCol = Math.max( 0, firstCol );
36125         
36126         var lastCol = Math.floor( lastX / this.columnWidth );
36127         // lastCol should not go over if multiple of columnWidth #425
36128         lastCol -= lastX % this.columnWidth ? 0 : 1;
36129         lastCol = Math.min( this.cols - 1, lastCol );
36130         
36131         // set colYs to bottom of the stamp
36132         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36133             stampSize.height;
36134             
36135         for ( var i = firstCol; i <= lastCol; i++ ) {
36136           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36137         }
36138     },
36139     */
36140     
36141     _getContainerSize : function()
36142     {
36143         this.maxY = Math.max.apply( Math, this.colYs );
36144         var size = {
36145             height: this.maxY
36146         };
36147       
36148         if ( this.isFitWidth ) {
36149             size.width = this._getContainerFitWidth();
36150         }
36151       
36152         return size;
36153     },
36154     
36155     _getContainerFitWidth : function()
36156     {
36157         var unusedCols = 0;
36158         // count unused columns
36159         var i = this.cols;
36160         while ( --i ) {
36161           if ( this.colYs[i] !== 0 ) {
36162             break;
36163           }
36164           unusedCols++;
36165         }
36166         // fit container to columns that have been used
36167         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36168     },
36169     
36170     needsResizeLayout : function()
36171     {
36172         var previousWidth = this.containerWidth;
36173         this.getContainerWidth();
36174         return previousWidth !== this.containerWidth;
36175     }
36176  
36177 });
36178
36179  
36180
36181  /*
36182  * - LGPL
36183  *
36184  * element
36185  * 
36186  */
36187
36188 /**
36189  * @class Roo.bootstrap.MasonryBrick
36190  * @extends Roo.bootstrap.Component
36191  * Bootstrap MasonryBrick class
36192  * 
36193  * @constructor
36194  * Create a new MasonryBrick
36195  * @param {Object} config The config object
36196  */
36197
36198 Roo.bootstrap.MasonryBrick = function(config){
36199     
36200     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36201     
36202     Roo.bootstrap.MasonryBrick.register(this);
36203     
36204     this.addEvents({
36205         // raw events
36206         /**
36207          * @event click
36208          * When a MasonryBrick is clcik
36209          * @param {Roo.bootstrap.MasonryBrick} this
36210          * @param {Roo.EventObject} e
36211          */
36212         "click" : true
36213     });
36214 };
36215
36216 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36217     
36218     /**
36219      * @cfg {String} title
36220      */   
36221     title : '',
36222     /**
36223      * @cfg {String} html
36224      */   
36225     html : '',
36226     /**
36227      * @cfg {String} bgimage
36228      */   
36229     bgimage : '',
36230     /**
36231      * @cfg {String} videourl
36232      */   
36233     videourl : '',
36234     /**
36235      * @cfg {String} cls
36236      */   
36237     cls : '',
36238     /**
36239      * @cfg {String} href
36240      */   
36241     href : '',
36242     /**
36243      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36244      */   
36245     size : 'xs',
36246     
36247     /**
36248      * @cfg {String} placetitle (center|bottom)
36249      */   
36250     placetitle : '',
36251     
36252     /**
36253      * @cfg {Boolean} isFitContainer defalut true
36254      */   
36255     isFitContainer : true, 
36256     
36257     /**
36258      * @cfg {Boolean} preventDefault defalut false
36259      */   
36260     preventDefault : false, 
36261     
36262     /**
36263      * @cfg {Boolean} inverse defalut false
36264      */   
36265     maskInverse : false, 
36266     
36267     getAutoCreate : function()
36268     {
36269         if(!this.isFitContainer){
36270             return this.getSplitAutoCreate();
36271         }
36272         
36273         var cls = 'masonry-brick masonry-brick-full';
36274         
36275         if(this.href.length){
36276             cls += ' masonry-brick-link';
36277         }
36278         
36279         if(this.bgimage.length){
36280             cls += ' masonry-brick-image';
36281         }
36282         
36283         if(this.maskInverse){
36284             cls += ' mask-inverse';
36285         }
36286         
36287         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36288             cls += ' enable-mask';
36289         }
36290         
36291         if(this.size){
36292             cls += ' masonry-' + this.size + '-brick';
36293         }
36294         
36295         if(this.placetitle.length){
36296             
36297             switch (this.placetitle) {
36298                 case 'center' :
36299                     cls += ' masonry-center-title';
36300                     break;
36301                 case 'bottom' :
36302                     cls += ' masonry-bottom-title';
36303                     break;
36304                 default:
36305                     break;
36306             }
36307             
36308         } else {
36309             if(!this.html.length && !this.bgimage.length){
36310                 cls += ' masonry-center-title';
36311             }
36312
36313             if(!this.html.length && this.bgimage.length){
36314                 cls += ' masonry-bottom-title';
36315             }
36316         }
36317         
36318         if(this.cls){
36319             cls += ' ' + this.cls;
36320         }
36321         
36322         var cfg = {
36323             tag: (this.href.length) ? 'a' : 'div',
36324             cls: cls,
36325             cn: [
36326                 {
36327                     tag: 'div',
36328                     cls: 'masonry-brick-mask'
36329                 },
36330                 {
36331                     tag: 'div',
36332                     cls: 'masonry-brick-paragraph',
36333                     cn: []
36334                 }
36335             ]
36336         };
36337         
36338         if(this.href.length){
36339             cfg.href = this.href;
36340         }
36341         
36342         var cn = cfg.cn[1].cn;
36343         
36344         if(this.title.length){
36345             cn.push({
36346                 tag: 'h4',
36347                 cls: 'masonry-brick-title',
36348                 html: this.title
36349             });
36350         }
36351         
36352         if(this.html.length){
36353             cn.push({
36354                 tag: 'p',
36355                 cls: 'masonry-brick-text',
36356                 html: this.html
36357             });
36358         }
36359         
36360         if (!this.title.length && !this.html.length) {
36361             cfg.cn[1].cls += ' hide';
36362         }
36363         
36364         if(this.bgimage.length){
36365             cfg.cn.push({
36366                 tag: 'img',
36367                 cls: 'masonry-brick-image-view',
36368                 src: this.bgimage
36369             });
36370         }
36371         
36372         if(this.videourl.length){
36373             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36374             // youtube support only?
36375             cfg.cn.push({
36376                 tag: 'iframe',
36377                 cls: 'masonry-brick-image-view',
36378                 src: vurl,
36379                 frameborder : 0,
36380                 allowfullscreen : true
36381             });
36382         }
36383         
36384         return cfg;
36385         
36386     },
36387     
36388     getSplitAutoCreate : function()
36389     {
36390         var cls = 'masonry-brick masonry-brick-split';
36391         
36392         if(this.href.length){
36393             cls += ' masonry-brick-link';
36394         }
36395         
36396         if(this.bgimage.length){
36397             cls += ' masonry-brick-image';
36398         }
36399         
36400         if(this.size){
36401             cls += ' masonry-' + this.size + '-brick';
36402         }
36403         
36404         switch (this.placetitle) {
36405             case 'center' :
36406                 cls += ' masonry-center-title';
36407                 break;
36408             case 'bottom' :
36409                 cls += ' masonry-bottom-title';
36410                 break;
36411             default:
36412                 if(!this.bgimage.length){
36413                     cls += ' masonry-center-title';
36414                 }
36415
36416                 if(this.bgimage.length){
36417                     cls += ' masonry-bottom-title';
36418                 }
36419                 break;
36420         }
36421         
36422         if(this.cls){
36423             cls += ' ' + this.cls;
36424         }
36425         
36426         var cfg = {
36427             tag: (this.href.length) ? 'a' : 'div',
36428             cls: cls,
36429             cn: [
36430                 {
36431                     tag: 'div',
36432                     cls: 'masonry-brick-split-head',
36433                     cn: [
36434                         {
36435                             tag: 'div',
36436                             cls: 'masonry-brick-paragraph',
36437                             cn: []
36438                         }
36439                     ]
36440                 },
36441                 {
36442                     tag: 'div',
36443                     cls: 'masonry-brick-split-body',
36444                     cn: []
36445                 }
36446             ]
36447         };
36448         
36449         if(this.href.length){
36450             cfg.href = this.href;
36451         }
36452         
36453         if(this.title.length){
36454             cfg.cn[0].cn[0].cn.push({
36455                 tag: 'h4',
36456                 cls: 'masonry-brick-title',
36457                 html: this.title
36458             });
36459         }
36460         
36461         if(this.html.length){
36462             cfg.cn[1].cn.push({
36463                 tag: 'p',
36464                 cls: 'masonry-brick-text',
36465                 html: this.html
36466             });
36467         }
36468
36469         if(this.bgimage.length){
36470             cfg.cn[0].cn.push({
36471                 tag: 'img',
36472                 cls: 'masonry-brick-image-view',
36473                 src: this.bgimage
36474             });
36475         }
36476         
36477         if(this.videourl.length){
36478             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36479             // youtube support only?
36480             cfg.cn[0].cn.cn.push({
36481                 tag: 'iframe',
36482                 cls: 'masonry-brick-image-view',
36483                 src: vurl,
36484                 frameborder : 0,
36485                 allowfullscreen : true
36486             });
36487         }
36488         
36489         return cfg;
36490     },
36491     
36492     initEvents: function() 
36493     {
36494         switch (this.size) {
36495             case 'xs' :
36496                 this.x = 1;
36497                 this.y = 1;
36498                 break;
36499             case 'sm' :
36500                 this.x = 2;
36501                 this.y = 2;
36502                 break;
36503             case 'md' :
36504             case 'md-left' :
36505             case 'md-right' :
36506                 this.x = 3;
36507                 this.y = 3;
36508                 break;
36509             case 'tall' :
36510                 this.x = 2;
36511                 this.y = 3;
36512                 break;
36513             case 'wide' :
36514                 this.x = 3;
36515                 this.y = 2;
36516                 break;
36517             case 'wide-thin' :
36518                 this.x = 3;
36519                 this.y = 1;
36520                 break;
36521                         
36522             default :
36523                 break;
36524         }
36525         
36526         if(Roo.isTouch){
36527             this.el.on('touchstart', this.onTouchStart, this);
36528             this.el.on('touchmove', this.onTouchMove, this);
36529             this.el.on('touchend', this.onTouchEnd, this);
36530             this.el.on('contextmenu', this.onContextMenu, this);
36531         } else {
36532             this.el.on('mouseenter'  ,this.enter, this);
36533             this.el.on('mouseleave', this.leave, this);
36534             this.el.on('click', this.onClick, this);
36535         }
36536         
36537         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36538             this.parent().bricks.push(this);   
36539         }
36540         
36541     },
36542     
36543     onClick: function(e, el)
36544     {
36545         var time = this.endTimer - this.startTimer;
36546         // Roo.log(e.preventDefault());
36547         if(Roo.isTouch){
36548             if(time > 1000){
36549                 e.preventDefault();
36550                 return;
36551             }
36552         }
36553         
36554         if(!this.preventDefault){
36555             return;
36556         }
36557         
36558         e.preventDefault();
36559         
36560         if (this.activeClass != '') {
36561             this.selectBrick();
36562         }
36563         
36564         this.fireEvent('click', this, e);
36565     },
36566     
36567     enter: function(e, el)
36568     {
36569         e.preventDefault();
36570         
36571         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36572             return;
36573         }
36574         
36575         if(this.bgimage.length && this.html.length){
36576             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36577         }
36578     },
36579     
36580     leave: function(e, el)
36581     {
36582         e.preventDefault();
36583         
36584         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36585             return;
36586         }
36587         
36588         if(this.bgimage.length && this.html.length){
36589             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36590         }
36591     },
36592     
36593     onTouchStart: function(e, el)
36594     {
36595 //        e.preventDefault();
36596         
36597         this.touchmoved = false;
36598         
36599         if(!this.isFitContainer){
36600             return;
36601         }
36602         
36603         if(!this.bgimage.length || !this.html.length){
36604             return;
36605         }
36606         
36607         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36608         
36609         this.timer = new Date().getTime();
36610         
36611     },
36612     
36613     onTouchMove: function(e, el)
36614     {
36615         this.touchmoved = true;
36616     },
36617     
36618     onContextMenu : function(e,el)
36619     {
36620         e.preventDefault();
36621         e.stopPropagation();
36622         return false;
36623     },
36624     
36625     onTouchEnd: function(e, el)
36626     {
36627 //        e.preventDefault();
36628         
36629         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36630         
36631             this.leave(e,el);
36632             
36633             return;
36634         }
36635         
36636         if(!this.bgimage.length || !this.html.length){
36637             
36638             if(this.href.length){
36639                 window.location.href = this.href;
36640             }
36641             
36642             return;
36643         }
36644         
36645         if(!this.isFitContainer){
36646             return;
36647         }
36648         
36649         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36650         
36651         window.location.href = this.href;
36652     },
36653     
36654     //selection on single brick only
36655     selectBrick : function() {
36656         
36657         if (!this.parentId) {
36658             return;
36659         }
36660         
36661         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36662         var index = m.selectedBrick.indexOf(this.id);
36663         
36664         if ( index > -1) {
36665             m.selectedBrick.splice(index,1);
36666             this.el.removeClass(this.activeClass);
36667             return;
36668         }
36669         
36670         for(var i = 0; i < m.selectedBrick.length; i++) {
36671             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36672             b.el.removeClass(b.activeClass);
36673         }
36674         
36675         m.selectedBrick = [];
36676         
36677         m.selectedBrick.push(this.id);
36678         this.el.addClass(this.activeClass);
36679         return;
36680     },
36681     
36682     isSelected : function(){
36683         return this.el.hasClass(this.activeClass);
36684         
36685     }
36686 });
36687
36688 Roo.apply(Roo.bootstrap.MasonryBrick, {
36689     
36690     //groups: {},
36691     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36692      /**
36693     * register a Masonry Brick
36694     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36695     */
36696     
36697     register : function(brick)
36698     {
36699         //this.groups[brick.id] = brick;
36700         this.groups.add(brick.id, brick);
36701     },
36702     /**
36703     * fetch a  masonry brick based on the masonry brick ID
36704     * @param {string} the masonry brick to add
36705     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36706     */
36707     
36708     get: function(brick_id) 
36709     {
36710         // if (typeof(this.groups[brick_id]) == 'undefined') {
36711         //     return false;
36712         // }
36713         // return this.groups[brick_id] ;
36714         
36715         if(this.groups.key(brick_id)) {
36716             return this.groups.key(brick_id);
36717         }
36718         
36719         return false;
36720     }
36721     
36722     
36723     
36724 });
36725
36726  /*
36727  * - LGPL
36728  *
36729  * element
36730  * 
36731  */
36732
36733 /**
36734  * @class Roo.bootstrap.Brick
36735  * @extends Roo.bootstrap.Component
36736  * Bootstrap Brick class
36737  * 
36738  * @constructor
36739  * Create a new Brick
36740  * @param {Object} config The config object
36741  */
36742
36743 Roo.bootstrap.Brick = function(config){
36744     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36745     
36746     this.addEvents({
36747         // raw events
36748         /**
36749          * @event click
36750          * When a Brick is click
36751          * @param {Roo.bootstrap.Brick} this
36752          * @param {Roo.EventObject} e
36753          */
36754         "click" : true
36755     });
36756 };
36757
36758 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36759     
36760     /**
36761      * @cfg {String} title
36762      */   
36763     title : '',
36764     /**
36765      * @cfg {String} html
36766      */   
36767     html : '',
36768     /**
36769      * @cfg {String} bgimage
36770      */   
36771     bgimage : '',
36772     /**
36773      * @cfg {String} cls
36774      */   
36775     cls : '',
36776     /**
36777      * @cfg {String} href
36778      */   
36779     href : '',
36780     /**
36781      * @cfg {String} video
36782      */   
36783     video : '',
36784     /**
36785      * @cfg {Boolean} square
36786      */   
36787     square : true,
36788     
36789     getAutoCreate : function()
36790     {
36791         var cls = 'roo-brick';
36792         
36793         if(this.href.length){
36794             cls += ' roo-brick-link';
36795         }
36796         
36797         if(this.bgimage.length){
36798             cls += ' roo-brick-image';
36799         }
36800         
36801         if(!this.html.length && !this.bgimage.length){
36802             cls += ' roo-brick-center-title';
36803         }
36804         
36805         if(!this.html.length && this.bgimage.length){
36806             cls += ' roo-brick-bottom-title';
36807         }
36808         
36809         if(this.cls){
36810             cls += ' ' + this.cls;
36811         }
36812         
36813         var cfg = {
36814             tag: (this.href.length) ? 'a' : 'div',
36815             cls: cls,
36816             cn: [
36817                 {
36818                     tag: 'div',
36819                     cls: 'roo-brick-paragraph',
36820                     cn: []
36821                 }
36822             ]
36823         };
36824         
36825         if(this.href.length){
36826             cfg.href = this.href;
36827         }
36828         
36829         var cn = cfg.cn[0].cn;
36830         
36831         if(this.title.length){
36832             cn.push({
36833                 tag: 'h4',
36834                 cls: 'roo-brick-title',
36835                 html: this.title
36836             });
36837         }
36838         
36839         if(this.html.length){
36840             cn.push({
36841                 tag: 'p',
36842                 cls: 'roo-brick-text',
36843                 html: this.html
36844             });
36845         } else {
36846             cn.cls += ' hide';
36847         }
36848         
36849         if(this.bgimage.length){
36850             cfg.cn.push({
36851                 tag: 'img',
36852                 cls: 'roo-brick-image-view',
36853                 src: this.bgimage
36854             });
36855         }
36856         
36857         return cfg;
36858     },
36859     
36860     initEvents: function() 
36861     {
36862         if(this.title.length || this.html.length){
36863             this.el.on('mouseenter'  ,this.enter, this);
36864             this.el.on('mouseleave', this.leave, this);
36865         }
36866         
36867         Roo.EventManager.onWindowResize(this.resize, this); 
36868         
36869         if(this.bgimage.length){
36870             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36871             this.imageEl.on('load', this.onImageLoad, this);
36872             return;
36873         }
36874         
36875         this.resize();
36876     },
36877     
36878     onImageLoad : function()
36879     {
36880         this.resize();
36881     },
36882     
36883     resize : function()
36884     {
36885         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36886         
36887         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36888         
36889         if(this.bgimage.length){
36890             var image = this.el.select('.roo-brick-image-view', true).first();
36891             
36892             image.setWidth(paragraph.getWidth());
36893             
36894             if(this.square){
36895                 image.setHeight(paragraph.getWidth());
36896             }
36897             
36898             this.el.setHeight(image.getHeight());
36899             paragraph.setHeight(image.getHeight());
36900             
36901         }
36902         
36903     },
36904     
36905     enter: function(e, el)
36906     {
36907         e.preventDefault();
36908         
36909         if(this.bgimage.length){
36910             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36911             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36912         }
36913     },
36914     
36915     leave: function(e, el)
36916     {
36917         e.preventDefault();
36918         
36919         if(this.bgimage.length){
36920             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36921             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36922         }
36923     }
36924     
36925 });
36926
36927  
36928
36929  /*
36930  * - LGPL
36931  *
36932  * Number field 
36933  */
36934
36935 /**
36936  * @class Roo.bootstrap.NumberField
36937  * @extends Roo.bootstrap.Input
36938  * Bootstrap NumberField class
36939  * 
36940  * 
36941  * 
36942  * 
36943  * @constructor
36944  * Create a new NumberField
36945  * @param {Object} config The config object
36946  */
36947
36948 Roo.bootstrap.NumberField = function(config){
36949     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36950 };
36951
36952 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36953     
36954     /**
36955      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36956      */
36957     allowDecimals : true,
36958     /**
36959      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36960      */
36961     decimalSeparator : ".",
36962     /**
36963      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36964      */
36965     decimalPrecision : 2,
36966     /**
36967      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36968      */
36969     allowNegative : true,
36970     
36971     /**
36972      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36973      */
36974     allowZero: true,
36975     /**
36976      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36977      */
36978     minValue : Number.NEGATIVE_INFINITY,
36979     /**
36980      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36981      */
36982     maxValue : Number.MAX_VALUE,
36983     /**
36984      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36985      */
36986     minText : "The minimum value for this field is {0}",
36987     /**
36988      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36989      */
36990     maxText : "The maximum value for this field is {0}",
36991     /**
36992      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36993      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36994      */
36995     nanText : "{0} is not a valid number",
36996     /**
36997      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36998      */
36999     thousandsDelimiter : false,
37000     /**
37001      * @cfg {String} valueAlign alignment of value
37002      */
37003     valueAlign : "left",
37004
37005     getAutoCreate : function()
37006     {
37007         var hiddenInput = {
37008             tag: 'input',
37009             type: 'hidden',
37010             id: Roo.id(),
37011             cls: 'hidden-number-input'
37012         };
37013         
37014         if (this.name) {
37015             hiddenInput.name = this.name;
37016         }
37017         
37018         this.name = '';
37019         
37020         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37021         
37022         this.name = hiddenInput.name;
37023         
37024         if(cfg.cn.length > 0) {
37025             cfg.cn.push(hiddenInput);
37026         }
37027         
37028         return cfg;
37029     },
37030
37031     // private
37032     initEvents : function()
37033     {   
37034         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37035         
37036         var allowed = "0123456789";
37037         
37038         if(this.allowDecimals){
37039             allowed += this.decimalSeparator;
37040         }
37041         
37042         if(this.allowNegative){
37043             allowed += "-";
37044         }
37045         
37046         if(this.thousandsDelimiter) {
37047             allowed += ",";
37048         }
37049         
37050         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37051         
37052         var keyPress = function(e){
37053             
37054             var k = e.getKey();
37055             
37056             var c = e.getCharCode();
37057             
37058             if(
37059                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37060                     allowed.indexOf(String.fromCharCode(c)) === -1
37061             ){
37062                 e.stopEvent();
37063                 return;
37064             }
37065             
37066             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37067                 return;
37068             }
37069             
37070             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37071                 e.stopEvent();
37072             }
37073         };
37074         
37075         this.el.on("keypress", keyPress, this);
37076     },
37077     
37078     validateValue : function(value)
37079     {
37080         
37081         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37082             return false;
37083         }
37084         
37085         var num = this.parseValue(value);
37086         
37087         if(isNaN(num)){
37088             this.markInvalid(String.format(this.nanText, value));
37089             return false;
37090         }
37091         
37092         if(num < this.minValue){
37093             this.markInvalid(String.format(this.minText, this.minValue));
37094             return false;
37095         }
37096         
37097         if(num > this.maxValue){
37098             this.markInvalid(String.format(this.maxText, this.maxValue));
37099             return false;
37100         }
37101         
37102         return true;
37103     },
37104
37105     getValue : function()
37106     {
37107         var v = this.hiddenEl().getValue();
37108         
37109         return this.fixPrecision(this.parseValue(v));
37110     },
37111
37112     parseValue : function(value)
37113     {
37114         if(this.thousandsDelimiter) {
37115             value += "";
37116             r = new RegExp(",", "g");
37117             value = value.replace(r, "");
37118         }
37119         
37120         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37121         return isNaN(value) ? '' : value;
37122     },
37123
37124     fixPrecision : function(value)
37125     {
37126         if(this.thousandsDelimiter) {
37127             value += "";
37128             r = new RegExp(",", "g");
37129             value = value.replace(r, "");
37130         }
37131         
37132         var nan = isNaN(value);
37133         
37134         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37135             return nan ? '' : value;
37136         }
37137         return parseFloat(value).toFixed(this.decimalPrecision);
37138     },
37139
37140     setValue : function(v)
37141     {
37142         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37143         
37144         this.value = v;
37145         
37146         if(this.rendered){
37147             
37148             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37149             
37150             this.inputEl().dom.value = (v == '') ? '' :
37151                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37152             
37153             if(!this.allowZero && v === '0') {
37154                 this.hiddenEl().dom.value = '';
37155                 this.inputEl().dom.value = '';
37156             }
37157             
37158             this.validate();
37159         }
37160     },
37161
37162     decimalPrecisionFcn : function(v)
37163     {
37164         return Math.floor(v);
37165     },
37166
37167     beforeBlur : function()
37168     {
37169         var v = this.parseValue(this.getRawValue());
37170         
37171         if(v || v === 0 || v === ''){
37172             this.setValue(v);
37173         }
37174     },
37175     
37176     hiddenEl : function()
37177     {
37178         return this.el.select('input.hidden-number-input',true).first();
37179     }
37180     
37181 });
37182
37183  
37184
37185 /*
37186 * Licence: LGPL
37187 */
37188
37189 /**
37190  * @class Roo.bootstrap.DocumentSlider
37191  * @extends Roo.bootstrap.Component
37192  * Bootstrap DocumentSlider class
37193  * 
37194  * @constructor
37195  * Create a new DocumentViewer
37196  * @param {Object} config The config object
37197  */
37198
37199 Roo.bootstrap.DocumentSlider = function(config){
37200     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37201     
37202     this.files = [];
37203     
37204     this.addEvents({
37205         /**
37206          * @event initial
37207          * Fire after initEvent
37208          * @param {Roo.bootstrap.DocumentSlider} this
37209          */
37210         "initial" : true,
37211         /**
37212          * @event update
37213          * Fire after update
37214          * @param {Roo.bootstrap.DocumentSlider} this
37215          */
37216         "update" : true,
37217         /**
37218          * @event click
37219          * Fire after click
37220          * @param {Roo.bootstrap.DocumentSlider} this
37221          */
37222         "click" : true
37223     });
37224 };
37225
37226 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37227     
37228     files : false,
37229     
37230     indicator : 0,
37231     
37232     getAutoCreate : function()
37233     {
37234         var cfg = {
37235             tag : 'div',
37236             cls : 'roo-document-slider',
37237             cn : [
37238                 {
37239                     tag : 'div',
37240                     cls : 'roo-document-slider-header',
37241                     cn : [
37242                         {
37243                             tag : 'div',
37244                             cls : 'roo-document-slider-header-title'
37245                         }
37246                     ]
37247                 },
37248                 {
37249                     tag : 'div',
37250                     cls : 'roo-document-slider-body',
37251                     cn : [
37252                         {
37253                             tag : 'div',
37254                             cls : 'roo-document-slider-prev',
37255                             cn : [
37256                                 {
37257                                     tag : 'i',
37258                                     cls : 'fa fa-chevron-left'
37259                                 }
37260                             ]
37261                         },
37262                         {
37263                             tag : 'div',
37264                             cls : 'roo-document-slider-thumb',
37265                             cn : [
37266                                 {
37267                                     tag : 'img',
37268                                     cls : 'roo-document-slider-image'
37269                                 }
37270                             ]
37271                         },
37272                         {
37273                             tag : 'div',
37274                             cls : 'roo-document-slider-next',
37275                             cn : [
37276                                 {
37277                                     tag : 'i',
37278                                     cls : 'fa fa-chevron-right'
37279                                 }
37280                             ]
37281                         }
37282                     ]
37283                 }
37284             ]
37285         };
37286         
37287         return cfg;
37288     },
37289     
37290     initEvents : function()
37291     {
37292         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37293         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37294         
37295         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37296         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37297         
37298         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37299         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37300         
37301         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37302         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37303         
37304         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37305         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37306         
37307         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37308         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37309         
37310         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37311         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37312         
37313         this.thumbEl.on('click', this.onClick, this);
37314         
37315         this.prevIndicator.on('click', this.prev, this);
37316         
37317         this.nextIndicator.on('click', this.next, this);
37318         
37319     },
37320     
37321     initial : function()
37322     {
37323         if(this.files.length){
37324             this.indicator = 1;
37325             this.update()
37326         }
37327         
37328         this.fireEvent('initial', this);
37329     },
37330     
37331     update : function()
37332     {
37333         this.imageEl.attr('src', this.files[this.indicator - 1]);
37334         
37335         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37336         
37337         this.prevIndicator.show();
37338         
37339         if(this.indicator == 1){
37340             this.prevIndicator.hide();
37341         }
37342         
37343         this.nextIndicator.show();
37344         
37345         if(this.indicator == this.files.length){
37346             this.nextIndicator.hide();
37347         }
37348         
37349         this.thumbEl.scrollTo('top');
37350         
37351         this.fireEvent('update', this);
37352     },
37353     
37354     onClick : function(e)
37355     {
37356         e.preventDefault();
37357         
37358         this.fireEvent('click', this);
37359     },
37360     
37361     prev : function(e)
37362     {
37363         e.preventDefault();
37364         
37365         this.indicator = Math.max(1, this.indicator - 1);
37366         
37367         this.update();
37368     },
37369     
37370     next : function(e)
37371     {
37372         e.preventDefault();
37373         
37374         this.indicator = Math.min(this.files.length, this.indicator + 1);
37375         
37376         this.update();
37377     }
37378 });
37379 /*
37380  * - LGPL
37381  *
37382  * RadioSet
37383  *
37384  *
37385  */
37386
37387 /**
37388  * @class Roo.bootstrap.RadioSet
37389  * @extends Roo.bootstrap.Input
37390  * Bootstrap RadioSet class
37391  * @cfg {String} indicatorpos (left|right) default left
37392  * @cfg {Boolean} inline (true|false) inline the element (default true)
37393  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37394  * @constructor
37395  * Create a new RadioSet
37396  * @param {Object} config The config object
37397  */
37398
37399 Roo.bootstrap.RadioSet = function(config){
37400     
37401     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37402     
37403     this.radioes = [];
37404     
37405     Roo.bootstrap.RadioSet.register(this);
37406     
37407     this.addEvents({
37408         /**
37409         * @event check
37410         * Fires when the element is checked or unchecked.
37411         * @param {Roo.bootstrap.RadioSet} this This radio
37412         * @param {Roo.bootstrap.Radio} item The checked item
37413         */
37414        check : true,
37415        /**
37416         * @event click
37417         * Fires when the element is click.
37418         * @param {Roo.bootstrap.RadioSet} this This radio set
37419         * @param {Roo.bootstrap.Radio} item The checked item
37420         * @param {Roo.EventObject} e The event object
37421         */
37422        click : true
37423     });
37424     
37425 };
37426
37427 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37428
37429     radioes : false,
37430     
37431     inline : true,
37432     
37433     weight : '',
37434     
37435     indicatorpos : 'left',
37436     
37437     getAutoCreate : function()
37438     {
37439         var label = {
37440             tag : 'label',
37441             cls : 'roo-radio-set-label',
37442             cn : [
37443                 {
37444                     tag : 'span',
37445                     html : this.fieldLabel
37446                 }
37447             ]
37448         };
37449         if (Roo.bootstrap.version == 3) {
37450             
37451             
37452             if(this.indicatorpos == 'left'){
37453                 label.cn.unshift({
37454                     tag : 'i',
37455                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37456                     tooltip : 'This field is required'
37457                 });
37458             } else {
37459                 label.cn.push({
37460                     tag : 'i',
37461                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37462                     tooltip : 'This field is required'
37463                 });
37464             }
37465         }
37466         var items = {
37467             tag : 'div',
37468             cls : 'roo-radio-set-items'
37469         };
37470         
37471         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37472         
37473         if (align === 'left' && this.fieldLabel.length) {
37474             
37475             items = {
37476                 cls : "roo-radio-set-right", 
37477                 cn: [
37478                     items
37479                 ]
37480             };
37481             
37482             if(this.labelWidth > 12){
37483                 label.style = "width: " + this.labelWidth + 'px';
37484             }
37485             
37486             if(this.labelWidth < 13 && this.labelmd == 0){
37487                 this.labelmd = this.labelWidth;
37488             }
37489             
37490             if(this.labellg > 0){
37491                 label.cls += ' col-lg-' + this.labellg;
37492                 items.cls += ' col-lg-' + (12 - this.labellg);
37493             }
37494             
37495             if(this.labelmd > 0){
37496                 label.cls += ' col-md-' + this.labelmd;
37497                 items.cls += ' col-md-' + (12 - this.labelmd);
37498             }
37499             
37500             if(this.labelsm > 0){
37501                 label.cls += ' col-sm-' + this.labelsm;
37502                 items.cls += ' col-sm-' + (12 - this.labelsm);
37503             }
37504             
37505             if(this.labelxs > 0){
37506                 label.cls += ' col-xs-' + this.labelxs;
37507                 items.cls += ' col-xs-' + (12 - this.labelxs);
37508             }
37509         }
37510         
37511         var cfg = {
37512             tag : 'div',
37513             cls : 'roo-radio-set',
37514             cn : [
37515                 {
37516                     tag : 'input',
37517                     cls : 'roo-radio-set-input',
37518                     type : 'hidden',
37519                     name : this.name,
37520                     value : this.value ? this.value :  ''
37521                 },
37522                 label,
37523                 items
37524             ]
37525         };
37526         
37527         if(this.weight.length){
37528             cfg.cls += ' roo-radio-' + this.weight;
37529         }
37530         
37531         if(this.inline) {
37532             cfg.cls += ' roo-radio-set-inline';
37533         }
37534         
37535         var settings=this;
37536         ['xs','sm','md','lg'].map(function(size){
37537             if (settings[size]) {
37538                 cfg.cls += ' col-' + size + '-' + settings[size];
37539             }
37540         });
37541         
37542         return cfg;
37543         
37544     },
37545
37546     initEvents : function()
37547     {
37548         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37549         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37550         
37551         if(!this.fieldLabel.length){
37552             this.labelEl.hide();
37553         }
37554         
37555         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37556         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37557         
37558         this.indicator = this.indicatorEl();
37559         
37560         if(this.indicator){
37561             this.indicator.addClass('invisible');
37562         }
37563         
37564         this.originalValue = this.getValue();
37565         
37566     },
37567     
37568     inputEl: function ()
37569     {
37570         return this.el.select('.roo-radio-set-input', true).first();
37571     },
37572     
37573     getChildContainer : function()
37574     {
37575         return this.itemsEl;
37576     },
37577     
37578     register : function(item)
37579     {
37580         this.radioes.push(item);
37581         
37582     },
37583     
37584     validate : function()
37585     {   
37586         if(this.getVisibilityEl().hasClass('hidden')){
37587             return true;
37588         }
37589         
37590         var valid = false;
37591         
37592         Roo.each(this.radioes, function(i){
37593             if(!i.checked){
37594                 return;
37595             }
37596             
37597             valid = true;
37598             return false;
37599         });
37600         
37601         if(this.allowBlank) {
37602             return true;
37603         }
37604         
37605         if(this.disabled || valid){
37606             this.markValid();
37607             return true;
37608         }
37609         
37610         this.markInvalid();
37611         return false;
37612         
37613     },
37614     
37615     markValid : function()
37616     {
37617         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37618             this.indicatorEl().removeClass('visible');
37619             this.indicatorEl().addClass('invisible');
37620         }
37621         
37622         
37623         if (Roo.bootstrap.version == 3) {
37624             this.el.removeClass([this.invalidClass, this.validClass]);
37625             this.el.addClass(this.validClass);
37626         } else {
37627             this.el.removeClass(['is-invalid','is-valid']);
37628             this.el.addClass(['is-valid']);
37629         }
37630         this.fireEvent('valid', this);
37631     },
37632     
37633     markInvalid : function(msg)
37634     {
37635         if(this.allowBlank || this.disabled){
37636             return;
37637         }
37638         
37639         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37640             this.indicatorEl().removeClass('invisible');
37641             this.indicatorEl().addClass('visible');
37642         }
37643         if (Roo.bootstrap.version == 3) {
37644             this.el.removeClass([this.invalidClass, this.validClass]);
37645             this.el.addClass(this.invalidClass);
37646         } else {
37647             this.el.removeClass(['is-invalid','is-valid']);
37648             this.el.addClass(['is-invalid']);
37649         }
37650         
37651         this.fireEvent('invalid', this, msg);
37652         
37653     },
37654     
37655     setValue : function(v, suppressEvent)
37656     {   
37657         if(this.value === v){
37658             return;
37659         }
37660         
37661         this.value = v;
37662         
37663         if(this.rendered){
37664             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37665         }
37666         
37667         Roo.each(this.radioes, function(i){
37668             i.checked = false;
37669             i.el.removeClass('checked');
37670         });
37671         
37672         Roo.each(this.radioes, function(i){
37673             
37674             if(i.value === v || i.value.toString() === v.toString()){
37675                 i.checked = true;
37676                 i.el.addClass('checked');
37677                 
37678                 if(suppressEvent !== true){
37679                     this.fireEvent('check', this, i);
37680                 }
37681                 
37682                 return false;
37683             }
37684             
37685         }, this);
37686         
37687         this.validate();
37688     },
37689     
37690     clearInvalid : function(){
37691         
37692         if(!this.el || this.preventMark){
37693             return;
37694         }
37695         
37696         this.el.removeClass([this.invalidClass]);
37697         
37698         this.fireEvent('valid', this);
37699     }
37700     
37701 });
37702
37703 Roo.apply(Roo.bootstrap.RadioSet, {
37704     
37705     groups: {},
37706     
37707     register : function(set)
37708     {
37709         this.groups[set.name] = set;
37710     },
37711     
37712     get: function(name) 
37713     {
37714         if (typeof(this.groups[name]) == 'undefined') {
37715             return false;
37716         }
37717         
37718         return this.groups[name] ;
37719     }
37720     
37721 });
37722 /*
37723  * Based on:
37724  * Ext JS Library 1.1.1
37725  * Copyright(c) 2006-2007, Ext JS, LLC.
37726  *
37727  * Originally Released Under LGPL - original licence link has changed is not relivant.
37728  *
37729  * Fork - LGPL
37730  * <script type="text/javascript">
37731  */
37732
37733
37734 /**
37735  * @class Roo.bootstrap.SplitBar
37736  * @extends Roo.util.Observable
37737  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37738  * <br><br>
37739  * Usage:
37740  * <pre><code>
37741 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37742                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37743 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37744 split.minSize = 100;
37745 split.maxSize = 600;
37746 split.animate = true;
37747 split.on('moved', splitterMoved);
37748 </code></pre>
37749  * @constructor
37750  * Create a new SplitBar
37751  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37752  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37753  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37754  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37755                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37756                         position of the SplitBar).
37757  */
37758 Roo.bootstrap.SplitBar = function(cfg){
37759     
37760     /** @private */
37761     
37762     //{
37763     //  dragElement : elm
37764     //  resizingElement: el,
37765         // optional..
37766     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37767     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37768         // existingProxy ???
37769     //}
37770     
37771     this.el = Roo.get(cfg.dragElement, true);
37772     this.el.dom.unselectable = "on";
37773     /** @private */
37774     this.resizingEl = Roo.get(cfg.resizingElement, true);
37775
37776     /**
37777      * @private
37778      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37779      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37780      * @type Number
37781      */
37782     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37783     
37784     /**
37785      * The minimum size of the resizing element. (Defaults to 0)
37786      * @type Number
37787      */
37788     this.minSize = 0;
37789     
37790     /**
37791      * The maximum size of the resizing element. (Defaults to 2000)
37792      * @type Number
37793      */
37794     this.maxSize = 2000;
37795     
37796     /**
37797      * Whether to animate the transition to the new size
37798      * @type Boolean
37799      */
37800     this.animate = false;
37801     
37802     /**
37803      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37804      * @type Boolean
37805      */
37806     this.useShim = false;
37807     
37808     /** @private */
37809     this.shim = null;
37810     
37811     if(!cfg.existingProxy){
37812         /** @private */
37813         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37814     }else{
37815         this.proxy = Roo.get(cfg.existingProxy).dom;
37816     }
37817     /** @private */
37818     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37819     
37820     /** @private */
37821     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37822     
37823     /** @private */
37824     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37825     
37826     /** @private */
37827     this.dragSpecs = {};
37828     
37829     /**
37830      * @private The adapter to use to positon and resize elements
37831      */
37832     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37833     this.adapter.init(this);
37834     
37835     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37836         /** @private */
37837         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37838         this.el.addClass("roo-splitbar-h");
37839     }else{
37840         /** @private */
37841         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37842         this.el.addClass("roo-splitbar-v");
37843     }
37844     
37845     this.addEvents({
37846         /**
37847          * @event resize
37848          * Fires when the splitter is moved (alias for {@link #event-moved})
37849          * @param {Roo.bootstrap.SplitBar} this
37850          * @param {Number} newSize the new width or height
37851          */
37852         "resize" : true,
37853         /**
37854          * @event moved
37855          * Fires when the splitter is moved
37856          * @param {Roo.bootstrap.SplitBar} this
37857          * @param {Number} newSize the new width or height
37858          */
37859         "moved" : true,
37860         /**
37861          * @event beforeresize
37862          * Fires before the splitter is dragged
37863          * @param {Roo.bootstrap.SplitBar} this
37864          */
37865         "beforeresize" : true,
37866
37867         "beforeapply" : true
37868     });
37869
37870     Roo.util.Observable.call(this);
37871 };
37872
37873 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37874     onStartProxyDrag : function(x, y){
37875         this.fireEvent("beforeresize", this);
37876         if(!this.overlay){
37877             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37878             o.unselectable();
37879             o.enableDisplayMode("block");
37880             // all splitbars share the same overlay
37881             Roo.bootstrap.SplitBar.prototype.overlay = o;
37882         }
37883         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37884         this.overlay.show();
37885         Roo.get(this.proxy).setDisplayed("block");
37886         var size = this.adapter.getElementSize(this);
37887         this.activeMinSize = this.getMinimumSize();;
37888         this.activeMaxSize = this.getMaximumSize();;
37889         var c1 = size - this.activeMinSize;
37890         var c2 = Math.max(this.activeMaxSize - size, 0);
37891         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37892             this.dd.resetConstraints();
37893             this.dd.setXConstraint(
37894                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37895                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37896             );
37897             this.dd.setYConstraint(0, 0);
37898         }else{
37899             this.dd.resetConstraints();
37900             this.dd.setXConstraint(0, 0);
37901             this.dd.setYConstraint(
37902                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37903                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37904             );
37905          }
37906         this.dragSpecs.startSize = size;
37907         this.dragSpecs.startPoint = [x, y];
37908         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37909     },
37910     
37911     /** 
37912      * @private Called after the drag operation by the DDProxy
37913      */
37914     onEndProxyDrag : function(e){
37915         Roo.get(this.proxy).setDisplayed(false);
37916         var endPoint = Roo.lib.Event.getXY(e);
37917         if(this.overlay){
37918             this.overlay.hide();
37919         }
37920         var newSize;
37921         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37922             newSize = this.dragSpecs.startSize + 
37923                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37924                     endPoint[0] - this.dragSpecs.startPoint[0] :
37925                     this.dragSpecs.startPoint[0] - endPoint[0]
37926                 );
37927         }else{
37928             newSize = this.dragSpecs.startSize + 
37929                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37930                     endPoint[1] - this.dragSpecs.startPoint[1] :
37931                     this.dragSpecs.startPoint[1] - endPoint[1]
37932                 );
37933         }
37934         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37935         if(newSize != this.dragSpecs.startSize){
37936             if(this.fireEvent('beforeapply', this, newSize) !== false){
37937                 this.adapter.setElementSize(this, newSize);
37938                 this.fireEvent("moved", this, newSize);
37939                 this.fireEvent("resize", this, newSize);
37940             }
37941         }
37942     },
37943     
37944     /**
37945      * Get the adapter this SplitBar uses
37946      * @return The adapter object
37947      */
37948     getAdapter : function(){
37949         return this.adapter;
37950     },
37951     
37952     /**
37953      * Set the adapter this SplitBar uses
37954      * @param {Object} adapter A SplitBar adapter object
37955      */
37956     setAdapter : function(adapter){
37957         this.adapter = adapter;
37958         this.adapter.init(this);
37959     },
37960     
37961     /**
37962      * Gets the minimum size for the resizing element
37963      * @return {Number} The minimum size
37964      */
37965     getMinimumSize : function(){
37966         return this.minSize;
37967     },
37968     
37969     /**
37970      * Sets the minimum size for the resizing element
37971      * @param {Number} minSize The minimum size
37972      */
37973     setMinimumSize : function(minSize){
37974         this.minSize = minSize;
37975     },
37976     
37977     /**
37978      * Gets the maximum size for the resizing element
37979      * @return {Number} The maximum size
37980      */
37981     getMaximumSize : function(){
37982         return this.maxSize;
37983     },
37984     
37985     /**
37986      * Sets the maximum size for the resizing element
37987      * @param {Number} maxSize The maximum size
37988      */
37989     setMaximumSize : function(maxSize){
37990         this.maxSize = maxSize;
37991     },
37992     
37993     /**
37994      * Sets the initialize size for the resizing element
37995      * @param {Number} size The initial size
37996      */
37997     setCurrentSize : function(size){
37998         var oldAnimate = this.animate;
37999         this.animate = false;
38000         this.adapter.setElementSize(this, size);
38001         this.animate = oldAnimate;
38002     },
38003     
38004     /**
38005      * Destroy this splitbar. 
38006      * @param {Boolean} removeEl True to remove the element
38007      */
38008     destroy : function(removeEl){
38009         if(this.shim){
38010             this.shim.remove();
38011         }
38012         this.dd.unreg();
38013         this.proxy.parentNode.removeChild(this.proxy);
38014         if(removeEl){
38015             this.el.remove();
38016         }
38017     }
38018 });
38019
38020 /**
38021  * @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.
38022  */
38023 Roo.bootstrap.SplitBar.createProxy = function(dir){
38024     var proxy = new Roo.Element(document.createElement("div"));
38025     proxy.unselectable();
38026     var cls = 'roo-splitbar-proxy';
38027     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38028     document.body.appendChild(proxy.dom);
38029     return proxy.dom;
38030 };
38031
38032 /** 
38033  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38034  * Default Adapter. It assumes the splitter and resizing element are not positioned
38035  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38036  */
38037 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38038 };
38039
38040 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38041     // do nothing for now
38042     init : function(s){
38043     
38044     },
38045     /**
38046      * Called before drag operations to get the current size of the resizing element. 
38047      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38048      */
38049      getElementSize : function(s){
38050         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38051             return s.resizingEl.getWidth();
38052         }else{
38053             return s.resizingEl.getHeight();
38054         }
38055     },
38056     
38057     /**
38058      * Called after drag operations to set the size of the resizing element.
38059      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38060      * @param {Number} newSize The new size to set
38061      * @param {Function} onComplete A function to be invoked when resizing is complete
38062      */
38063     setElementSize : function(s, newSize, onComplete){
38064         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38065             if(!s.animate){
38066                 s.resizingEl.setWidth(newSize);
38067                 if(onComplete){
38068                     onComplete(s, newSize);
38069                 }
38070             }else{
38071                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38072             }
38073         }else{
38074             
38075             if(!s.animate){
38076                 s.resizingEl.setHeight(newSize);
38077                 if(onComplete){
38078                     onComplete(s, newSize);
38079                 }
38080             }else{
38081                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38082             }
38083         }
38084     }
38085 };
38086
38087 /** 
38088  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38089  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38090  * Adapter that  moves the splitter element to align with the resized sizing element. 
38091  * Used with an absolute positioned SplitBar.
38092  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38093  * document.body, make sure you assign an id to the body element.
38094  */
38095 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38096     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38097     this.container = Roo.get(container);
38098 };
38099
38100 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38101     init : function(s){
38102         this.basic.init(s);
38103     },
38104     
38105     getElementSize : function(s){
38106         return this.basic.getElementSize(s);
38107     },
38108     
38109     setElementSize : function(s, newSize, onComplete){
38110         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38111     },
38112     
38113     moveSplitter : function(s){
38114         var yes = Roo.bootstrap.SplitBar;
38115         switch(s.placement){
38116             case yes.LEFT:
38117                 s.el.setX(s.resizingEl.getRight());
38118                 break;
38119             case yes.RIGHT:
38120                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38121                 break;
38122             case yes.TOP:
38123                 s.el.setY(s.resizingEl.getBottom());
38124                 break;
38125             case yes.BOTTOM:
38126                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38127                 break;
38128         }
38129     }
38130 };
38131
38132 /**
38133  * Orientation constant - Create a vertical SplitBar
38134  * @static
38135  * @type Number
38136  */
38137 Roo.bootstrap.SplitBar.VERTICAL = 1;
38138
38139 /**
38140  * Orientation constant - Create a horizontal SplitBar
38141  * @static
38142  * @type Number
38143  */
38144 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38145
38146 /**
38147  * Placement constant - The resizing element is to the left of the splitter element
38148  * @static
38149  * @type Number
38150  */
38151 Roo.bootstrap.SplitBar.LEFT = 1;
38152
38153 /**
38154  * Placement constant - The resizing element is to the right of the splitter element
38155  * @static
38156  * @type Number
38157  */
38158 Roo.bootstrap.SplitBar.RIGHT = 2;
38159
38160 /**
38161  * Placement constant - The resizing element is positioned above the splitter element
38162  * @static
38163  * @type Number
38164  */
38165 Roo.bootstrap.SplitBar.TOP = 3;
38166
38167 /**
38168  * Placement constant - The resizing element is positioned under splitter element
38169  * @static
38170  * @type Number
38171  */
38172 Roo.bootstrap.SplitBar.BOTTOM = 4;
38173 Roo.namespace("Roo.bootstrap.layout");/*
38174  * Based on:
38175  * Ext JS Library 1.1.1
38176  * Copyright(c) 2006-2007, Ext JS, LLC.
38177  *
38178  * Originally Released Under LGPL - original licence link has changed is not relivant.
38179  *
38180  * Fork - LGPL
38181  * <script type="text/javascript">
38182  */
38183
38184 /**
38185  * @class Roo.bootstrap.layout.Manager
38186  * @extends Roo.bootstrap.Component
38187  * Base class for layout managers.
38188  */
38189 Roo.bootstrap.layout.Manager = function(config)
38190 {
38191     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38192
38193
38194
38195
38196
38197     /** false to disable window resize monitoring @type Boolean */
38198     this.monitorWindowResize = true;
38199     this.regions = {};
38200     this.addEvents({
38201         /**
38202          * @event layout
38203          * Fires when a layout is performed.
38204          * @param {Roo.LayoutManager} this
38205          */
38206         "layout" : true,
38207         /**
38208          * @event regionresized
38209          * Fires when the user resizes a region.
38210          * @param {Roo.LayoutRegion} region The resized region
38211          * @param {Number} newSize The new size (width for east/west, height for north/south)
38212          */
38213         "regionresized" : true,
38214         /**
38215          * @event regioncollapsed
38216          * Fires when a region is collapsed.
38217          * @param {Roo.LayoutRegion} region The collapsed region
38218          */
38219         "regioncollapsed" : true,
38220         /**
38221          * @event regionexpanded
38222          * Fires when a region is expanded.
38223          * @param {Roo.LayoutRegion} region The expanded region
38224          */
38225         "regionexpanded" : true
38226     });
38227     this.updating = false;
38228
38229     if (config.el) {
38230         this.el = Roo.get(config.el);
38231         this.initEvents();
38232     }
38233
38234 };
38235
38236 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38237
38238
38239     regions : null,
38240
38241     monitorWindowResize : true,
38242
38243
38244     updating : false,
38245
38246
38247     onRender : function(ct, position)
38248     {
38249         if(!this.el){
38250             this.el = Roo.get(ct);
38251             this.initEvents();
38252         }
38253         //this.fireEvent('render',this);
38254     },
38255
38256
38257     initEvents: function()
38258     {
38259
38260
38261         // ie scrollbar fix
38262         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38263             document.body.scroll = "no";
38264         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38265             this.el.position('relative');
38266         }
38267         this.id = this.el.id;
38268         this.el.addClass("roo-layout-container");
38269         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38270         if(this.el.dom != document.body ) {
38271             this.el.on('resize', this.layout,this);
38272             this.el.on('show', this.layout,this);
38273         }
38274
38275     },
38276
38277     /**
38278      * Returns true if this layout is currently being updated
38279      * @return {Boolean}
38280      */
38281     isUpdating : function(){
38282         return this.updating;
38283     },
38284
38285     /**
38286      * Suspend the LayoutManager from doing auto-layouts while
38287      * making multiple add or remove calls
38288      */
38289     beginUpdate : function(){
38290         this.updating = true;
38291     },
38292
38293     /**
38294      * Restore auto-layouts and optionally disable the manager from performing a layout
38295      * @param {Boolean} noLayout true to disable a layout update
38296      */
38297     endUpdate : function(noLayout){
38298         this.updating = false;
38299         if(!noLayout){
38300             this.layout();
38301         }
38302     },
38303
38304     layout: function(){
38305         // abstract...
38306     },
38307
38308     onRegionResized : function(region, newSize){
38309         this.fireEvent("regionresized", region, newSize);
38310         this.layout();
38311     },
38312
38313     onRegionCollapsed : function(region){
38314         this.fireEvent("regioncollapsed", region);
38315     },
38316
38317     onRegionExpanded : function(region){
38318         this.fireEvent("regionexpanded", region);
38319     },
38320
38321     /**
38322      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38323      * performs box-model adjustments.
38324      * @return {Object} The size as an object {width: (the width), height: (the height)}
38325      */
38326     getViewSize : function()
38327     {
38328         var size;
38329         if(this.el.dom != document.body){
38330             size = this.el.getSize();
38331         }else{
38332             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38333         }
38334         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38335         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38336         return size;
38337     },
38338
38339     /**
38340      * Returns the Element this layout is bound to.
38341      * @return {Roo.Element}
38342      */
38343     getEl : function(){
38344         return this.el;
38345     },
38346
38347     /**
38348      * Returns the specified region.
38349      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38350      * @return {Roo.LayoutRegion}
38351      */
38352     getRegion : function(target){
38353         return this.regions[target.toLowerCase()];
38354     },
38355
38356     onWindowResize : function(){
38357         if(this.monitorWindowResize){
38358             this.layout();
38359         }
38360     }
38361 });
38362 /*
38363  * Based on:
38364  * Ext JS Library 1.1.1
38365  * Copyright(c) 2006-2007, Ext JS, LLC.
38366  *
38367  * Originally Released Under LGPL - original licence link has changed is not relivant.
38368  *
38369  * Fork - LGPL
38370  * <script type="text/javascript">
38371  */
38372 /**
38373  * @class Roo.bootstrap.layout.Border
38374  * @extends Roo.bootstrap.layout.Manager
38375  * @builder-top
38376  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38377  * please see: examples/bootstrap/nested.html<br><br>
38378  
38379 <b>The container the layout is rendered into can be either the body element or any other element.
38380 If it is not the body element, the container needs to either be an absolute positioned element,
38381 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38382 the container size if it is not the body element.</b>
38383
38384 * @constructor
38385 * Create a new Border
38386 * @param {Object} config Configuration options
38387  */
38388 Roo.bootstrap.layout.Border = function(config){
38389     config = config || {};
38390     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38391     
38392     
38393     
38394     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38395         if(config[region]){
38396             config[region].region = region;
38397             this.addRegion(config[region]);
38398         }
38399     },this);
38400     
38401 };
38402
38403 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38404
38405 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38406     
38407     parent : false, // this might point to a 'nest' or a ???
38408     
38409     /**
38410      * Creates and adds a new region if it doesn't already exist.
38411      * @param {String} target The target region key (north, south, east, west or center).
38412      * @param {Object} config The regions config object
38413      * @return {BorderLayoutRegion} The new region
38414      */
38415     addRegion : function(config)
38416     {
38417         if(!this.regions[config.region]){
38418             var r = this.factory(config);
38419             this.bindRegion(r);
38420         }
38421         return this.regions[config.region];
38422     },
38423
38424     // private (kinda)
38425     bindRegion : function(r){
38426         this.regions[r.config.region] = r;
38427         
38428         r.on("visibilitychange",    this.layout, this);
38429         r.on("paneladded",          this.layout, this);
38430         r.on("panelremoved",        this.layout, this);
38431         r.on("invalidated",         this.layout, this);
38432         r.on("resized",             this.onRegionResized, this);
38433         r.on("collapsed",           this.onRegionCollapsed, this);
38434         r.on("expanded",            this.onRegionExpanded, this);
38435     },
38436
38437     /**
38438      * Performs a layout update.
38439      */
38440     layout : function()
38441     {
38442         if(this.updating) {
38443             return;
38444         }
38445         
38446         // render all the rebions if they have not been done alreayd?
38447         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38448             if(this.regions[region] && !this.regions[region].bodyEl){
38449                 this.regions[region].onRender(this.el)
38450             }
38451         },this);
38452         
38453         var size = this.getViewSize();
38454         var w = size.width;
38455         var h = size.height;
38456         var centerW = w;
38457         var centerH = h;
38458         var centerY = 0;
38459         var centerX = 0;
38460         //var x = 0, y = 0;
38461
38462         var rs = this.regions;
38463         var north = rs["north"];
38464         var south = rs["south"]; 
38465         var west = rs["west"];
38466         var east = rs["east"];
38467         var center = rs["center"];
38468         //if(this.hideOnLayout){ // not supported anymore
38469             //c.el.setStyle("display", "none");
38470         //}
38471         if(north && north.isVisible()){
38472             var b = north.getBox();
38473             var m = north.getMargins();
38474             b.width = w - (m.left+m.right);
38475             b.x = m.left;
38476             b.y = m.top;
38477             centerY = b.height + b.y + m.bottom;
38478             centerH -= centerY;
38479             north.updateBox(this.safeBox(b));
38480         }
38481         if(south && south.isVisible()){
38482             var b = south.getBox();
38483             var m = south.getMargins();
38484             b.width = w - (m.left+m.right);
38485             b.x = m.left;
38486             var totalHeight = (b.height + m.top + m.bottom);
38487             b.y = h - totalHeight + m.top;
38488             centerH -= totalHeight;
38489             south.updateBox(this.safeBox(b));
38490         }
38491         if(west && west.isVisible()){
38492             var b = west.getBox();
38493             var m = west.getMargins();
38494             b.height = centerH - (m.top+m.bottom);
38495             b.x = m.left;
38496             b.y = centerY + m.top;
38497             var totalWidth = (b.width + m.left + m.right);
38498             centerX += totalWidth;
38499             centerW -= totalWidth;
38500             west.updateBox(this.safeBox(b));
38501         }
38502         if(east && east.isVisible()){
38503             var b = east.getBox();
38504             var m = east.getMargins();
38505             b.height = centerH - (m.top+m.bottom);
38506             var totalWidth = (b.width + m.left + m.right);
38507             b.x = w - totalWidth + m.left;
38508             b.y = centerY + m.top;
38509             centerW -= totalWidth;
38510             east.updateBox(this.safeBox(b));
38511         }
38512         if(center){
38513             var m = center.getMargins();
38514             var centerBox = {
38515                 x: centerX + m.left,
38516                 y: centerY + m.top,
38517                 width: centerW - (m.left+m.right),
38518                 height: centerH - (m.top+m.bottom)
38519             };
38520             //if(this.hideOnLayout){
38521                 //center.el.setStyle("display", "block");
38522             //}
38523             center.updateBox(this.safeBox(centerBox));
38524         }
38525         this.el.repaint();
38526         this.fireEvent("layout", this);
38527     },
38528
38529     // private
38530     safeBox : function(box){
38531         box.width = Math.max(0, box.width);
38532         box.height = Math.max(0, box.height);
38533         return box;
38534     },
38535
38536     /**
38537      * Adds a ContentPanel (or subclass) to this layout.
38538      * @param {String} target The target region key (north, south, east, west or center).
38539      * @param {Roo.ContentPanel} panel The panel to add
38540      * @return {Roo.ContentPanel} The added panel
38541      */
38542     add : function(target, panel){
38543          
38544         target = target.toLowerCase();
38545         return this.regions[target].add(panel);
38546     },
38547
38548     /**
38549      * Remove a ContentPanel (or subclass) to this layout.
38550      * @param {String} target The target region key (north, south, east, west or center).
38551      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38552      * @return {Roo.ContentPanel} The removed panel
38553      */
38554     remove : function(target, panel){
38555         target = target.toLowerCase();
38556         return this.regions[target].remove(panel);
38557     },
38558
38559     /**
38560      * Searches all regions for a panel with the specified id
38561      * @param {String} panelId
38562      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38563      */
38564     findPanel : function(panelId){
38565         var rs = this.regions;
38566         for(var target in rs){
38567             if(typeof rs[target] != "function"){
38568                 var p = rs[target].getPanel(panelId);
38569                 if(p){
38570                     return p;
38571                 }
38572             }
38573         }
38574         return null;
38575     },
38576
38577     /**
38578      * Searches all regions for a panel with the specified id and activates (shows) it.
38579      * @param {String/ContentPanel} panelId The panels id or the panel itself
38580      * @return {Roo.ContentPanel} The shown panel or null
38581      */
38582     showPanel : function(panelId) {
38583       var rs = this.regions;
38584       for(var target in rs){
38585          var r = rs[target];
38586          if(typeof r != "function"){
38587             if(r.hasPanel(panelId)){
38588                return r.showPanel(panelId);
38589             }
38590          }
38591       }
38592       return null;
38593    },
38594
38595    /**
38596      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38597      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38598      */
38599    /*
38600     restoreState : function(provider){
38601         if(!provider){
38602             provider = Roo.state.Manager;
38603         }
38604         var sm = new Roo.LayoutStateManager();
38605         sm.init(this, provider);
38606     },
38607 */
38608  
38609  
38610     /**
38611      * Adds a xtype elements to the layout.
38612      * <pre><code>
38613
38614 layout.addxtype({
38615        xtype : 'ContentPanel',
38616        region: 'west',
38617        items: [ .... ]
38618    }
38619 );
38620
38621 layout.addxtype({
38622         xtype : 'NestedLayoutPanel',
38623         region: 'west',
38624         layout: {
38625            center: { },
38626            west: { }   
38627         },
38628         items : [ ... list of content panels or nested layout panels.. ]
38629    }
38630 );
38631 </code></pre>
38632      * @param {Object} cfg Xtype definition of item to add.
38633      */
38634     addxtype : function(cfg)
38635     {
38636         // basically accepts a pannel...
38637         // can accept a layout region..!?!?
38638         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38639         
38640         
38641         // theory?  children can only be panels??
38642         
38643         //if (!cfg.xtype.match(/Panel$/)) {
38644         //    return false;
38645         //}
38646         var ret = false;
38647         
38648         if (typeof(cfg.region) == 'undefined') {
38649             Roo.log("Failed to add Panel, region was not set");
38650             Roo.log(cfg);
38651             return false;
38652         }
38653         var region = cfg.region;
38654         delete cfg.region;
38655         
38656           
38657         var xitems = [];
38658         if (cfg.items) {
38659             xitems = cfg.items;
38660             delete cfg.items;
38661         }
38662         var nb = false;
38663         
38664         if ( region == 'center') {
38665             Roo.log("Center: " + cfg.title);
38666         }
38667         
38668         
38669         switch(cfg.xtype) 
38670         {
38671             case 'Content':  // ContentPanel (el, cfg)
38672             case 'Scroll':  // ContentPanel (el, cfg)
38673             case 'View': 
38674                 cfg.autoCreate = cfg.autoCreate || true;
38675                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38676                 //} else {
38677                 //    var el = this.el.createChild();
38678                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38679                 //}
38680                 
38681                 this.add(region, ret);
38682                 break;
38683             
38684             /*
38685             case 'TreePanel': // our new panel!
38686                 cfg.el = this.el.createChild();
38687                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38688                 this.add(region, ret);
38689                 break;
38690             */
38691             
38692             case 'Nest': 
38693                 // create a new Layout (which is  a Border Layout...
38694                 
38695                 var clayout = cfg.layout;
38696                 clayout.el  = this.el.createChild();
38697                 clayout.items   = clayout.items  || [];
38698                 
38699                 delete cfg.layout;
38700                 
38701                 // replace this exitems with the clayout ones..
38702                 xitems = clayout.items;
38703                  
38704                 // force background off if it's in center...
38705                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38706                     cfg.background = false;
38707                 }
38708                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38709                 
38710                 
38711                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38712                 //console.log('adding nested layout panel '  + cfg.toSource());
38713                 this.add(region, ret);
38714                 nb = {}; /// find first...
38715                 break;
38716             
38717             case 'Grid':
38718                 
38719                 // needs grid and region
38720                 
38721                 //var el = this.getRegion(region).el.createChild();
38722                 /*
38723                  *var el = this.el.createChild();
38724                 // create the grid first...
38725                 cfg.grid.container = el;
38726                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38727                 */
38728                 
38729                 if (region == 'center' && this.active ) {
38730                     cfg.background = false;
38731                 }
38732                 
38733                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38734                 
38735                 this.add(region, ret);
38736                 /*
38737                 if (cfg.background) {
38738                     // render grid on panel activation (if panel background)
38739                     ret.on('activate', function(gp) {
38740                         if (!gp.grid.rendered) {
38741                     //        gp.grid.render(el);
38742                         }
38743                     });
38744                 } else {
38745                   //  cfg.grid.render(el);
38746                 }
38747                 */
38748                 break;
38749            
38750            
38751             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38752                 // it was the old xcomponent building that caused this before.
38753                 // espeically if border is the top element in the tree.
38754                 ret = this;
38755                 break; 
38756                 
38757                     
38758                 
38759                 
38760                 
38761             default:
38762                 /*
38763                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38764                     
38765                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38766                     this.add(region, ret);
38767                 } else {
38768                 */
38769                     Roo.log(cfg);
38770                     throw "Can not add '" + cfg.xtype + "' to Border";
38771                     return null;
38772              
38773                                 
38774              
38775         }
38776         this.beginUpdate();
38777         // add children..
38778         var region = '';
38779         var abn = {};
38780         Roo.each(xitems, function(i)  {
38781             region = nb && i.region ? i.region : false;
38782             
38783             var add = ret.addxtype(i);
38784            
38785             if (region) {
38786                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38787                 if (!i.background) {
38788                     abn[region] = nb[region] ;
38789                 }
38790             }
38791             
38792         });
38793         this.endUpdate();
38794
38795         // make the last non-background panel active..
38796         //if (nb) { Roo.log(abn); }
38797         if (nb) {
38798             
38799             for(var r in abn) {
38800                 region = this.getRegion(r);
38801                 if (region) {
38802                     // tried using nb[r], but it does not work..
38803                      
38804                     region.showPanel(abn[r]);
38805                    
38806                 }
38807             }
38808         }
38809         return ret;
38810         
38811     },
38812     
38813     
38814 // private
38815     factory : function(cfg)
38816     {
38817         
38818         var validRegions = Roo.bootstrap.layout.Border.regions;
38819
38820         var target = cfg.region;
38821         cfg.mgr = this;
38822         
38823         var r = Roo.bootstrap.layout;
38824         Roo.log(target);
38825         switch(target){
38826             case "north":
38827                 return new r.North(cfg);
38828             case "south":
38829                 return new r.South(cfg);
38830             case "east":
38831                 return new r.East(cfg);
38832             case "west":
38833                 return new r.West(cfg);
38834             case "center":
38835                 return new r.Center(cfg);
38836         }
38837         throw 'Layout region "'+target+'" not supported.';
38838     }
38839     
38840     
38841 });
38842  /*
38843  * Based on:
38844  * Ext JS Library 1.1.1
38845  * Copyright(c) 2006-2007, Ext JS, LLC.
38846  *
38847  * Originally Released Under LGPL - original licence link has changed is not relivant.
38848  *
38849  * Fork - LGPL
38850  * <script type="text/javascript">
38851  */
38852  
38853 /**
38854  * @class Roo.bootstrap.layout.Basic
38855  * @extends Roo.util.Observable
38856  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38857  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38858  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38859  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38860  * @cfg {string}   region  the region that it inhabits..
38861  * @cfg {bool}   skipConfig skip config?
38862  * 
38863
38864  */
38865 Roo.bootstrap.layout.Basic = function(config){
38866     
38867     this.mgr = config.mgr;
38868     
38869     this.position = config.region;
38870     
38871     var skipConfig = config.skipConfig;
38872     
38873     this.events = {
38874         /**
38875          * @scope Roo.BasicLayoutRegion
38876          */
38877         
38878         /**
38879          * @event beforeremove
38880          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38881          * @param {Roo.LayoutRegion} this
38882          * @param {Roo.ContentPanel} panel The panel
38883          * @param {Object} e The cancel event object
38884          */
38885         "beforeremove" : true,
38886         /**
38887          * @event invalidated
38888          * Fires when the layout for this region is changed.
38889          * @param {Roo.LayoutRegion} this
38890          */
38891         "invalidated" : true,
38892         /**
38893          * @event visibilitychange
38894          * Fires when this region is shown or hidden 
38895          * @param {Roo.LayoutRegion} this
38896          * @param {Boolean} visibility true or false
38897          */
38898         "visibilitychange" : true,
38899         /**
38900          * @event paneladded
38901          * Fires when a panel is added. 
38902          * @param {Roo.LayoutRegion} this
38903          * @param {Roo.ContentPanel} panel The panel
38904          */
38905         "paneladded" : true,
38906         /**
38907          * @event panelremoved
38908          * Fires when a panel is removed. 
38909          * @param {Roo.LayoutRegion} this
38910          * @param {Roo.ContentPanel} panel The panel
38911          */
38912         "panelremoved" : true,
38913         /**
38914          * @event beforecollapse
38915          * Fires when this region before collapse.
38916          * @param {Roo.LayoutRegion} this
38917          */
38918         "beforecollapse" : true,
38919         /**
38920          * @event collapsed
38921          * Fires when this region is collapsed.
38922          * @param {Roo.LayoutRegion} this
38923          */
38924         "collapsed" : true,
38925         /**
38926          * @event expanded
38927          * Fires when this region is expanded.
38928          * @param {Roo.LayoutRegion} this
38929          */
38930         "expanded" : true,
38931         /**
38932          * @event slideshow
38933          * Fires when this region is slid into view.
38934          * @param {Roo.LayoutRegion} this
38935          */
38936         "slideshow" : true,
38937         /**
38938          * @event slidehide
38939          * Fires when this region slides out of view. 
38940          * @param {Roo.LayoutRegion} this
38941          */
38942         "slidehide" : true,
38943         /**
38944          * @event panelactivated
38945          * Fires when a panel is activated. 
38946          * @param {Roo.LayoutRegion} this
38947          * @param {Roo.ContentPanel} panel The activated panel
38948          */
38949         "panelactivated" : true,
38950         /**
38951          * @event resized
38952          * Fires when the user resizes this region. 
38953          * @param {Roo.LayoutRegion} this
38954          * @param {Number} newSize The new size (width for east/west, height for north/south)
38955          */
38956         "resized" : true
38957     };
38958     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38959     this.panels = new Roo.util.MixedCollection();
38960     this.panels.getKey = this.getPanelId.createDelegate(this);
38961     this.box = null;
38962     this.activePanel = null;
38963     // ensure listeners are added...
38964     
38965     if (config.listeners || config.events) {
38966         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38967             listeners : config.listeners || {},
38968             events : config.events || {}
38969         });
38970     }
38971     
38972     if(skipConfig !== true){
38973         this.applyConfig(config);
38974     }
38975 };
38976
38977 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38978 {
38979     getPanelId : function(p){
38980         return p.getId();
38981     },
38982     
38983     applyConfig : function(config){
38984         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38985         this.config = config;
38986         
38987     },
38988     
38989     /**
38990      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38991      * the width, for horizontal (north, south) the height.
38992      * @param {Number} newSize The new width or height
38993      */
38994     resizeTo : function(newSize){
38995         var el = this.el ? this.el :
38996                  (this.activePanel ? this.activePanel.getEl() : null);
38997         if(el){
38998             switch(this.position){
38999                 case "east":
39000                 case "west":
39001                     el.setWidth(newSize);
39002                     this.fireEvent("resized", this, newSize);
39003                 break;
39004                 case "north":
39005                 case "south":
39006                     el.setHeight(newSize);
39007                     this.fireEvent("resized", this, newSize);
39008                 break;                
39009             }
39010         }
39011     },
39012     
39013     getBox : function(){
39014         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39015     },
39016     
39017     getMargins : function(){
39018         return this.margins;
39019     },
39020     
39021     updateBox : function(box){
39022         this.box = box;
39023         var el = this.activePanel.getEl();
39024         el.dom.style.left = box.x + "px";
39025         el.dom.style.top = box.y + "px";
39026         this.activePanel.setSize(box.width, box.height);
39027     },
39028     
39029     /**
39030      * Returns the container element for this region.
39031      * @return {Roo.Element}
39032      */
39033     getEl : function(){
39034         return this.activePanel;
39035     },
39036     
39037     /**
39038      * Returns true if this region is currently visible.
39039      * @return {Boolean}
39040      */
39041     isVisible : function(){
39042         return this.activePanel ? true : false;
39043     },
39044     
39045     setActivePanel : function(panel){
39046         panel = this.getPanel(panel);
39047         if(this.activePanel && this.activePanel != panel){
39048             this.activePanel.setActiveState(false);
39049             this.activePanel.getEl().setLeftTop(-10000,-10000);
39050         }
39051         this.activePanel = panel;
39052         panel.setActiveState(true);
39053         if(this.box){
39054             panel.setSize(this.box.width, this.box.height);
39055         }
39056         this.fireEvent("panelactivated", this, panel);
39057         this.fireEvent("invalidated");
39058     },
39059     
39060     /**
39061      * Show the specified panel.
39062      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39063      * @return {Roo.ContentPanel} The shown panel or null
39064      */
39065     showPanel : function(panel){
39066         panel = this.getPanel(panel);
39067         if(panel){
39068             this.setActivePanel(panel);
39069         }
39070         return panel;
39071     },
39072     
39073     /**
39074      * Get the active panel for this region.
39075      * @return {Roo.ContentPanel} The active panel or null
39076      */
39077     getActivePanel : function(){
39078         return this.activePanel;
39079     },
39080     
39081     /**
39082      * Add the passed ContentPanel(s)
39083      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39084      * @return {Roo.ContentPanel} The panel added (if only one was added)
39085      */
39086     add : function(panel){
39087         if(arguments.length > 1){
39088             for(var i = 0, len = arguments.length; i < len; i++) {
39089                 this.add(arguments[i]);
39090             }
39091             return null;
39092         }
39093         if(this.hasPanel(panel)){
39094             this.showPanel(panel);
39095             return panel;
39096         }
39097         var el = panel.getEl();
39098         if(el.dom.parentNode != this.mgr.el.dom){
39099             this.mgr.el.dom.appendChild(el.dom);
39100         }
39101         if(panel.setRegion){
39102             panel.setRegion(this);
39103         }
39104         this.panels.add(panel);
39105         el.setStyle("position", "absolute");
39106         if(!panel.background){
39107             this.setActivePanel(panel);
39108             if(this.config.initialSize && this.panels.getCount()==1){
39109                 this.resizeTo(this.config.initialSize);
39110             }
39111         }
39112         this.fireEvent("paneladded", this, panel);
39113         return panel;
39114     },
39115     
39116     /**
39117      * Returns true if the panel is in this region.
39118      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39119      * @return {Boolean}
39120      */
39121     hasPanel : function(panel){
39122         if(typeof panel == "object"){ // must be panel obj
39123             panel = panel.getId();
39124         }
39125         return this.getPanel(panel) ? true : false;
39126     },
39127     
39128     /**
39129      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39130      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39131      * @param {Boolean} preservePanel Overrides the config preservePanel option
39132      * @return {Roo.ContentPanel} The panel that was removed
39133      */
39134     remove : function(panel, preservePanel){
39135         panel = this.getPanel(panel);
39136         if(!panel){
39137             return null;
39138         }
39139         var e = {};
39140         this.fireEvent("beforeremove", this, panel, e);
39141         if(e.cancel === true){
39142             return null;
39143         }
39144         var panelId = panel.getId();
39145         this.panels.removeKey(panelId);
39146         return panel;
39147     },
39148     
39149     /**
39150      * Returns the panel specified or null if it's not in this region.
39151      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39152      * @return {Roo.ContentPanel}
39153      */
39154     getPanel : function(id){
39155         if(typeof id == "object"){ // must be panel obj
39156             return id;
39157         }
39158         return this.panels.get(id);
39159     },
39160     
39161     /**
39162      * Returns this regions position (north/south/east/west/center).
39163      * @return {String} 
39164      */
39165     getPosition: function(){
39166         return this.position;    
39167     }
39168 });/*
39169  * Based on:
39170  * Ext JS Library 1.1.1
39171  * Copyright(c) 2006-2007, Ext JS, LLC.
39172  *
39173  * Originally Released Under LGPL - original licence link has changed is not relivant.
39174  *
39175  * Fork - LGPL
39176  * <script type="text/javascript">
39177  */
39178  
39179 /**
39180  * @class Roo.bootstrap.layout.Region
39181  * @extends Roo.bootstrap.layout.Basic
39182  * This class represents a region in a layout manager.
39183  
39184  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39185  * @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})
39186  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39187  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39188  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39189  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39190  * @cfg {String}    title           The title for the region (overrides panel titles)
39191  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39192  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39193  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39194  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39195  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39196  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39197  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39198  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39199  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39200  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39201
39202  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39203  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39204  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39205  * @cfg {Number}    width           For East/West panels
39206  * @cfg {Number}    height          For North/South panels
39207  * @cfg {Boolean}   split           To show the splitter
39208  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39209  * 
39210  * @cfg {string}   cls             Extra CSS classes to add to region
39211  * 
39212  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39213  * @cfg {string}   region  the region that it inhabits..
39214  *
39215
39216  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39217  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39218
39219  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39220  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39221  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39222  */
39223 Roo.bootstrap.layout.Region = function(config)
39224 {
39225     this.applyConfig(config);
39226
39227     var mgr = config.mgr;
39228     var pos = config.region;
39229     config.skipConfig = true;
39230     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39231     
39232     if (mgr.el) {
39233         this.onRender(mgr.el);   
39234     }
39235      
39236     this.visible = true;
39237     this.collapsed = false;
39238     this.unrendered_panels = [];
39239 };
39240
39241 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39242
39243     position: '', // set by wrapper (eg. north/south etc..)
39244     unrendered_panels : null,  // unrendered panels.
39245     
39246     tabPosition : false,
39247     
39248     mgr: false, // points to 'Border'
39249     
39250     
39251     createBody : function(){
39252         /** This region's body element 
39253         * @type Roo.Element */
39254         this.bodyEl = this.el.createChild({
39255                 tag: "div",
39256                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39257         });
39258     },
39259
39260     onRender: function(ctr, pos)
39261     {
39262         var dh = Roo.DomHelper;
39263         /** This region's container element 
39264         * @type Roo.Element */
39265         this.el = dh.append(ctr.dom, {
39266                 tag: "div",
39267                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39268             }, true);
39269         /** This region's title element 
39270         * @type Roo.Element */
39271     
39272         this.titleEl = dh.append(this.el.dom,  {
39273                 tag: "div",
39274                 unselectable: "on",
39275                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39276                 children:[
39277                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39278                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39279                 ]
39280             }, true);
39281         
39282         this.titleEl.enableDisplayMode();
39283         /** This region's title text element 
39284         * @type HTMLElement */
39285         this.titleTextEl = this.titleEl.dom.firstChild;
39286         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39287         /*
39288         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39289         this.closeBtn.enableDisplayMode();
39290         this.closeBtn.on("click", this.closeClicked, this);
39291         this.closeBtn.hide();
39292     */
39293         this.createBody(this.config);
39294         if(this.config.hideWhenEmpty){
39295             this.hide();
39296             this.on("paneladded", this.validateVisibility, this);
39297             this.on("panelremoved", this.validateVisibility, this);
39298         }
39299         if(this.autoScroll){
39300             this.bodyEl.setStyle("overflow", "auto");
39301         }else{
39302             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39303         }
39304         //if(c.titlebar !== false){
39305             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39306                 this.titleEl.hide();
39307             }else{
39308                 this.titleEl.show();
39309                 if(this.config.title){
39310                     this.titleTextEl.innerHTML = this.config.title;
39311                 }
39312             }
39313         //}
39314         if(this.config.collapsed){
39315             this.collapse(true);
39316         }
39317         if(this.config.hidden){
39318             this.hide();
39319         }
39320         
39321         if (this.unrendered_panels && this.unrendered_panels.length) {
39322             for (var i =0;i< this.unrendered_panels.length; i++) {
39323                 this.add(this.unrendered_panels[i]);
39324             }
39325             this.unrendered_panels = null;
39326             
39327         }
39328         
39329     },
39330     
39331     applyConfig : function(c)
39332     {
39333         /*
39334          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39335             var dh = Roo.DomHelper;
39336             if(c.titlebar !== false){
39337                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39338                 this.collapseBtn.on("click", this.collapse, this);
39339                 this.collapseBtn.enableDisplayMode();
39340                 /*
39341                 if(c.showPin === true || this.showPin){
39342                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39343                     this.stickBtn.enableDisplayMode();
39344                     this.stickBtn.on("click", this.expand, this);
39345                     this.stickBtn.hide();
39346                 }
39347                 
39348             }
39349             */
39350             /** This region's collapsed element
39351             * @type Roo.Element */
39352             /*
39353              *
39354             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39355                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39356             ]}, true);
39357             
39358             if(c.floatable !== false){
39359                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39360                this.collapsedEl.on("click", this.collapseClick, this);
39361             }
39362
39363             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39364                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39365                    id: "message", unselectable: "on", style:{"float":"left"}});
39366                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39367              }
39368             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39369             this.expandBtn.on("click", this.expand, this);
39370             
39371         }
39372         
39373         if(this.collapseBtn){
39374             this.collapseBtn.setVisible(c.collapsible == true);
39375         }
39376         
39377         this.cmargins = c.cmargins || this.cmargins ||
39378                          (this.position == "west" || this.position == "east" ?
39379                              {top: 0, left: 2, right:2, bottom: 0} :
39380                              {top: 2, left: 0, right:0, bottom: 2});
39381         */
39382         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39383         
39384         
39385         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39386         
39387         this.autoScroll = c.autoScroll || false;
39388         
39389         
39390        
39391         
39392         this.duration = c.duration || .30;
39393         this.slideDuration = c.slideDuration || .45;
39394         this.config = c;
39395        
39396     },
39397     /**
39398      * Returns true if this region is currently visible.
39399      * @return {Boolean}
39400      */
39401     isVisible : function(){
39402         return this.visible;
39403     },
39404
39405     /**
39406      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39407      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39408      */
39409     //setCollapsedTitle : function(title){
39410     //    title = title || "&#160;";
39411      //   if(this.collapsedTitleTextEl){
39412       //      this.collapsedTitleTextEl.innerHTML = title;
39413        // }
39414     //},
39415
39416     getBox : function(){
39417         var b;
39418       //  if(!this.collapsed){
39419             b = this.el.getBox(false, true);
39420        // }else{
39421           //  b = this.collapsedEl.getBox(false, true);
39422         //}
39423         return b;
39424     },
39425
39426     getMargins : function(){
39427         return this.margins;
39428         //return this.collapsed ? this.cmargins : this.margins;
39429     },
39430 /*
39431     highlight : function(){
39432         this.el.addClass("x-layout-panel-dragover");
39433     },
39434
39435     unhighlight : function(){
39436         this.el.removeClass("x-layout-panel-dragover");
39437     },
39438 */
39439     updateBox : function(box)
39440     {
39441         if (!this.bodyEl) {
39442             return; // not rendered yet..
39443         }
39444         
39445         this.box = box;
39446         if(!this.collapsed){
39447             this.el.dom.style.left = box.x + "px";
39448             this.el.dom.style.top = box.y + "px";
39449             this.updateBody(box.width, box.height);
39450         }else{
39451             this.collapsedEl.dom.style.left = box.x + "px";
39452             this.collapsedEl.dom.style.top = box.y + "px";
39453             this.collapsedEl.setSize(box.width, box.height);
39454         }
39455         if(this.tabs){
39456             this.tabs.autoSizeTabs();
39457         }
39458     },
39459
39460     updateBody : function(w, h)
39461     {
39462         if(w !== null){
39463             this.el.setWidth(w);
39464             w -= this.el.getBorderWidth("rl");
39465             if(this.config.adjustments){
39466                 w += this.config.adjustments[0];
39467             }
39468         }
39469         if(h !== null && h > 0){
39470             this.el.setHeight(h);
39471             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39472             h -= this.el.getBorderWidth("tb");
39473             if(this.config.adjustments){
39474                 h += this.config.adjustments[1];
39475             }
39476             this.bodyEl.setHeight(h);
39477             if(this.tabs){
39478                 h = this.tabs.syncHeight(h);
39479             }
39480         }
39481         if(this.panelSize){
39482             w = w !== null ? w : this.panelSize.width;
39483             h = h !== null ? h : this.panelSize.height;
39484         }
39485         if(this.activePanel){
39486             var el = this.activePanel.getEl();
39487             w = w !== null ? w : el.getWidth();
39488             h = h !== null ? h : el.getHeight();
39489             this.panelSize = {width: w, height: h};
39490             this.activePanel.setSize(w, h);
39491         }
39492         if(Roo.isIE && this.tabs){
39493             this.tabs.el.repaint();
39494         }
39495     },
39496
39497     /**
39498      * Returns the container element for this region.
39499      * @return {Roo.Element}
39500      */
39501     getEl : function(){
39502         return this.el;
39503     },
39504
39505     /**
39506      * Hides this region.
39507      */
39508     hide : function(){
39509         //if(!this.collapsed){
39510             this.el.dom.style.left = "-2000px";
39511             this.el.hide();
39512         //}else{
39513          //   this.collapsedEl.dom.style.left = "-2000px";
39514          //   this.collapsedEl.hide();
39515        // }
39516         this.visible = false;
39517         this.fireEvent("visibilitychange", this, false);
39518     },
39519
39520     /**
39521      * Shows this region if it was previously hidden.
39522      */
39523     show : function(){
39524         //if(!this.collapsed){
39525             this.el.show();
39526         //}else{
39527         //    this.collapsedEl.show();
39528        // }
39529         this.visible = true;
39530         this.fireEvent("visibilitychange", this, true);
39531     },
39532 /*
39533     closeClicked : function(){
39534         if(this.activePanel){
39535             this.remove(this.activePanel);
39536         }
39537     },
39538
39539     collapseClick : function(e){
39540         if(this.isSlid){
39541            e.stopPropagation();
39542            this.slideIn();
39543         }else{
39544            e.stopPropagation();
39545            this.slideOut();
39546         }
39547     },
39548 */
39549     /**
39550      * Collapses this region.
39551      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39552      */
39553     /*
39554     collapse : function(skipAnim, skipCheck = false){
39555         if(this.collapsed) {
39556             return;
39557         }
39558         
39559         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39560             
39561             this.collapsed = true;
39562             if(this.split){
39563                 this.split.el.hide();
39564             }
39565             if(this.config.animate && skipAnim !== true){
39566                 this.fireEvent("invalidated", this);
39567                 this.animateCollapse();
39568             }else{
39569                 this.el.setLocation(-20000,-20000);
39570                 this.el.hide();
39571                 this.collapsedEl.show();
39572                 this.fireEvent("collapsed", this);
39573                 this.fireEvent("invalidated", this);
39574             }
39575         }
39576         
39577     },
39578 */
39579     animateCollapse : function(){
39580         // overridden
39581     },
39582
39583     /**
39584      * Expands this region if it was previously collapsed.
39585      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39586      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39587      */
39588     /*
39589     expand : function(e, skipAnim){
39590         if(e) {
39591             e.stopPropagation();
39592         }
39593         if(!this.collapsed || this.el.hasActiveFx()) {
39594             return;
39595         }
39596         if(this.isSlid){
39597             this.afterSlideIn();
39598             skipAnim = true;
39599         }
39600         this.collapsed = false;
39601         if(this.config.animate && skipAnim !== true){
39602             this.animateExpand();
39603         }else{
39604             this.el.show();
39605             if(this.split){
39606                 this.split.el.show();
39607             }
39608             this.collapsedEl.setLocation(-2000,-2000);
39609             this.collapsedEl.hide();
39610             this.fireEvent("invalidated", this);
39611             this.fireEvent("expanded", this);
39612         }
39613     },
39614 */
39615     animateExpand : function(){
39616         // overridden
39617     },
39618
39619     initTabs : function()
39620     {
39621         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39622         
39623         var ts = new Roo.bootstrap.panel.Tabs({
39624             el: this.bodyEl.dom,
39625             region : this,
39626             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39627             disableTooltips: this.config.disableTabTips,
39628             toolbar : this.config.toolbar
39629         });
39630         
39631         if(this.config.hideTabs){
39632             ts.stripWrap.setDisplayed(false);
39633         }
39634         this.tabs = ts;
39635         ts.resizeTabs = this.config.resizeTabs === true;
39636         ts.minTabWidth = this.config.minTabWidth || 40;
39637         ts.maxTabWidth = this.config.maxTabWidth || 250;
39638         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39639         ts.monitorResize = false;
39640         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39641         ts.bodyEl.addClass('roo-layout-tabs-body');
39642         this.panels.each(this.initPanelAsTab, this);
39643     },
39644
39645     initPanelAsTab : function(panel){
39646         var ti = this.tabs.addTab(
39647             panel.getEl().id,
39648             panel.getTitle(),
39649             null,
39650             this.config.closeOnTab && panel.isClosable(),
39651             panel.tpl
39652         );
39653         if(panel.tabTip !== undefined){
39654             ti.setTooltip(panel.tabTip);
39655         }
39656         ti.on("activate", function(){
39657               this.setActivePanel(panel);
39658         }, this);
39659         
39660         if(this.config.closeOnTab){
39661             ti.on("beforeclose", function(t, e){
39662                 e.cancel = true;
39663                 this.remove(panel);
39664             }, this);
39665         }
39666         
39667         panel.tabItem = ti;
39668         
39669         return ti;
39670     },
39671
39672     updatePanelTitle : function(panel, title)
39673     {
39674         if(this.activePanel == panel){
39675             this.updateTitle(title);
39676         }
39677         if(this.tabs){
39678             var ti = this.tabs.getTab(panel.getEl().id);
39679             ti.setText(title);
39680             if(panel.tabTip !== undefined){
39681                 ti.setTooltip(panel.tabTip);
39682             }
39683         }
39684     },
39685
39686     updateTitle : function(title){
39687         if(this.titleTextEl && !this.config.title){
39688             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39689         }
39690     },
39691
39692     setActivePanel : function(panel)
39693     {
39694         panel = this.getPanel(panel);
39695         if(this.activePanel && this.activePanel != panel){
39696             if(this.activePanel.setActiveState(false) === false){
39697                 return;
39698             }
39699         }
39700         this.activePanel = panel;
39701         panel.setActiveState(true);
39702         if(this.panelSize){
39703             panel.setSize(this.panelSize.width, this.panelSize.height);
39704         }
39705         if(this.closeBtn){
39706             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39707         }
39708         this.updateTitle(panel.getTitle());
39709         if(this.tabs){
39710             this.fireEvent("invalidated", this);
39711         }
39712         this.fireEvent("panelactivated", this, panel);
39713     },
39714
39715     /**
39716      * Shows the specified panel.
39717      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39718      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39719      */
39720     showPanel : function(panel)
39721     {
39722         panel = this.getPanel(panel);
39723         if(panel){
39724             if(this.tabs){
39725                 var tab = this.tabs.getTab(panel.getEl().id);
39726                 if(tab.isHidden()){
39727                     this.tabs.unhideTab(tab.id);
39728                 }
39729                 tab.activate();
39730             }else{
39731                 this.setActivePanel(panel);
39732             }
39733         }
39734         return panel;
39735     },
39736
39737     /**
39738      * Get the active panel for this region.
39739      * @return {Roo.ContentPanel} The active panel or null
39740      */
39741     getActivePanel : function(){
39742         return this.activePanel;
39743     },
39744
39745     validateVisibility : function(){
39746         if(this.panels.getCount() < 1){
39747             this.updateTitle("&#160;");
39748             this.closeBtn.hide();
39749             this.hide();
39750         }else{
39751             if(!this.isVisible()){
39752                 this.show();
39753             }
39754         }
39755     },
39756
39757     /**
39758      * Adds the passed ContentPanel(s) to this region.
39759      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39760      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39761      */
39762     add : function(panel)
39763     {
39764         if(arguments.length > 1){
39765             for(var i = 0, len = arguments.length; i < len; i++) {
39766                 this.add(arguments[i]);
39767             }
39768             return null;
39769         }
39770         
39771         // if we have not been rendered yet, then we can not really do much of this..
39772         if (!this.bodyEl) {
39773             this.unrendered_panels.push(panel);
39774             return panel;
39775         }
39776         
39777         
39778         
39779         
39780         if(this.hasPanel(panel)){
39781             this.showPanel(panel);
39782             return panel;
39783         }
39784         panel.setRegion(this);
39785         this.panels.add(panel);
39786        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39787             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39788             // and hide them... ???
39789             this.bodyEl.dom.appendChild(panel.getEl().dom);
39790             if(panel.background !== true){
39791                 this.setActivePanel(panel);
39792             }
39793             this.fireEvent("paneladded", this, panel);
39794             return panel;
39795         }
39796         */
39797         if(!this.tabs){
39798             this.initTabs();
39799         }else{
39800             this.initPanelAsTab(panel);
39801         }
39802         
39803         
39804         if(panel.background !== true){
39805             this.tabs.activate(panel.getEl().id);
39806         }
39807         this.fireEvent("paneladded", this, panel);
39808         return panel;
39809     },
39810
39811     /**
39812      * Hides the tab for the specified panel.
39813      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39814      */
39815     hidePanel : function(panel){
39816         if(this.tabs && (panel = this.getPanel(panel))){
39817             this.tabs.hideTab(panel.getEl().id);
39818         }
39819     },
39820
39821     /**
39822      * Unhides the tab for a previously hidden panel.
39823      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39824      */
39825     unhidePanel : function(panel){
39826         if(this.tabs && (panel = this.getPanel(panel))){
39827             this.tabs.unhideTab(panel.getEl().id);
39828         }
39829     },
39830
39831     clearPanels : function(){
39832         while(this.panels.getCount() > 0){
39833              this.remove(this.panels.first());
39834         }
39835     },
39836
39837     /**
39838      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39839      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39840      * @param {Boolean} preservePanel Overrides the config preservePanel option
39841      * @return {Roo.ContentPanel} The panel that was removed
39842      */
39843     remove : function(panel, preservePanel)
39844     {
39845         panel = this.getPanel(panel);
39846         if(!panel){
39847             return null;
39848         }
39849         var e = {};
39850         this.fireEvent("beforeremove", this, panel, e);
39851         if(e.cancel === true){
39852             return null;
39853         }
39854         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39855         var panelId = panel.getId();
39856         this.panels.removeKey(panelId);
39857         if(preservePanel){
39858             document.body.appendChild(panel.getEl().dom);
39859         }
39860         if(this.tabs){
39861             this.tabs.removeTab(panel.getEl().id);
39862         }else if (!preservePanel){
39863             this.bodyEl.dom.removeChild(panel.getEl().dom);
39864         }
39865         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39866             var p = this.panels.first();
39867             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39868             tempEl.appendChild(p.getEl().dom);
39869             this.bodyEl.update("");
39870             this.bodyEl.dom.appendChild(p.getEl().dom);
39871             tempEl = null;
39872             this.updateTitle(p.getTitle());
39873             this.tabs = null;
39874             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39875             this.setActivePanel(p);
39876         }
39877         panel.setRegion(null);
39878         if(this.activePanel == panel){
39879             this.activePanel = null;
39880         }
39881         if(this.config.autoDestroy !== false && preservePanel !== true){
39882             try{panel.destroy();}catch(e){}
39883         }
39884         this.fireEvent("panelremoved", this, panel);
39885         return panel;
39886     },
39887
39888     /**
39889      * Returns the TabPanel component used by this region
39890      * @return {Roo.TabPanel}
39891      */
39892     getTabs : function(){
39893         return this.tabs;
39894     },
39895
39896     createTool : function(parentEl, className){
39897         var btn = Roo.DomHelper.append(parentEl, {
39898             tag: "div",
39899             cls: "x-layout-tools-button",
39900             children: [ {
39901                 tag: "div",
39902                 cls: "roo-layout-tools-button-inner " + className,
39903                 html: "&#160;"
39904             }]
39905         }, true);
39906         btn.addClassOnOver("roo-layout-tools-button-over");
39907         return btn;
39908     }
39909 });/*
39910  * Based on:
39911  * Ext JS Library 1.1.1
39912  * Copyright(c) 2006-2007, Ext JS, LLC.
39913  *
39914  * Originally Released Under LGPL - original licence link has changed is not relivant.
39915  *
39916  * Fork - LGPL
39917  * <script type="text/javascript">
39918  */
39919  
39920
39921
39922 /**
39923  * @class Roo.SplitLayoutRegion
39924  * @extends Roo.LayoutRegion
39925  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39926  */
39927 Roo.bootstrap.layout.Split = function(config){
39928     this.cursor = config.cursor;
39929     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39930 };
39931
39932 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39933 {
39934     splitTip : "Drag to resize.",
39935     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39936     useSplitTips : false,
39937
39938     applyConfig : function(config){
39939         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39940     },
39941     
39942     onRender : function(ctr,pos) {
39943         
39944         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39945         if(!this.config.split){
39946             return;
39947         }
39948         if(!this.split){
39949             
39950             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39951                             tag: "div",
39952                             id: this.el.id + "-split",
39953                             cls: "roo-layout-split roo-layout-split-"+this.position,
39954                             html: "&#160;"
39955             });
39956             /** The SplitBar for this region 
39957             * @type Roo.SplitBar */
39958             // does not exist yet...
39959             Roo.log([this.position, this.orientation]);
39960             
39961             this.split = new Roo.bootstrap.SplitBar({
39962                 dragElement : splitEl,
39963                 resizingElement: this.el,
39964                 orientation : this.orientation
39965             });
39966             
39967             this.split.on("moved", this.onSplitMove, this);
39968             this.split.useShim = this.config.useShim === true;
39969             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39970             if(this.useSplitTips){
39971                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39972             }
39973             //if(config.collapsible){
39974             //    this.split.el.on("dblclick", this.collapse,  this);
39975             //}
39976         }
39977         if(typeof this.config.minSize != "undefined"){
39978             this.split.minSize = this.config.minSize;
39979         }
39980         if(typeof this.config.maxSize != "undefined"){
39981             this.split.maxSize = this.config.maxSize;
39982         }
39983         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39984             this.hideSplitter();
39985         }
39986         
39987     },
39988
39989     getHMaxSize : function(){
39990          var cmax = this.config.maxSize || 10000;
39991          var center = this.mgr.getRegion("center");
39992          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39993     },
39994
39995     getVMaxSize : function(){
39996          var cmax = this.config.maxSize || 10000;
39997          var center = this.mgr.getRegion("center");
39998          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39999     },
40000
40001     onSplitMove : function(split, newSize){
40002         this.fireEvent("resized", this, newSize);
40003     },
40004     
40005     /** 
40006      * Returns the {@link Roo.SplitBar} for this region.
40007      * @return {Roo.SplitBar}
40008      */
40009     getSplitBar : function(){
40010         return this.split;
40011     },
40012     
40013     hide : function(){
40014         this.hideSplitter();
40015         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40016     },
40017
40018     hideSplitter : function(){
40019         if(this.split){
40020             this.split.el.setLocation(-2000,-2000);
40021             this.split.el.hide();
40022         }
40023     },
40024
40025     show : function(){
40026         if(this.split){
40027             this.split.el.show();
40028         }
40029         Roo.bootstrap.layout.Split.superclass.show.call(this);
40030     },
40031     
40032     beforeSlide: function(){
40033         if(Roo.isGecko){// firefox overflow auto bug workaround
40034             this.bodyEl.clip();
40035             if(this.tabs) {
40036                 this.tabs.bodyEl.clip();
40037             }
40038             if(this.activePanel){
40039                 this.activePanel.getEl().clip();
40040                 
40041                 if(this.activePanel.beforeSlide){
40042                     this.activePanel.beforeSlide();
40043                 }
40044             }
40045         }
40046     },
40047     
40048     afterSlide : function(){
40049         if(Roo.isGecko){// firefox overflow auto bug workaround
40050             this.bodyEl.unclip();
40051             if(this.tabs) {
40052                 this.tabs.bodyEl.unclip();
40053             }
40054             if(this.activePanel){
40055                 this.activePanel.getEl().unclip();
40056                 if(this.activePanel.afterSlide){
40057                     this.activePanel.afterSlide();
40058                 }
40059             }
40060         }
40061     },
40062
40063     initAutoHide : function(){
40064         if(this.autoHide !== false){
40065             if(!this.autoHideHd){
40066                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40067                 this.autoHideHd = {
40068                     "mouseout": function(e){
40069                         if(!e.within(this.el, true)){
40070                             st.delay(500);
40071                         }
40072                     },
40073                     "mouseover" : function(e){
40074                         st.cancel();
40075                     },
40076                     scope : this
40077                 };
40078             }
40079             this.el.on(this.autoHideHd);
40080         }
40081     },
40082
40083     clearAutoHide : function(){
40084         if(this.autoHide !== false){
40085             this.el.un("mouseout", this.autoHideHd.mouseout);
40086             this.el.un("mouseover", this.autoHideHd.mouseover);
40087         }
40088     },
40089
40090     clearMonitor : function(){
40091         Roo.get(document).un("click", this.slideInIf, this);
40092     },
40093
40094     // these names are backwards but not changed for compat
40095     slideOut : function(){
40096         if(this.isSlid || this.el.hasActiveFx()){
40097             return;
40098         }
40099         this.isSlid = true;
40100         if(this.collapseBtn){
40101             this.collapseBtn.hide();
40102         }
40103         this.closeBtnState = this.closeBtn.getStyle('display');
40104         this.closeBtn.hide();
40105         if(this.stickBtn){
40106             this.stickBtn.show();
40107         }
40108         this.el.show();
40109         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40110         this.beforeSlide();
40111         this.el.setStyle("z-index", 10001);
40112         this.el.slideIn(this.getSlideAnchor(), {
40113             callback: function(){
40114                 this.afterSlide();
40115                 this.initAutoHide();
40116                 Roo.get(document).on("click", this.slideInIf, this);
40117                 this.fireEvent("slideshow", this);
40118             },
40119             scope: this,
40120             block: true
40121         });
40122     },
40123
40124     afterSlideIn : function(){
40125         this.clearAutoHide();
40126         this.isSlid = false;
40127         this.clearMonitor();
40128         this.el.setStyle("z-index", "");
40129         if(this.collapseBtn){
40130             this.collapseBtn.show();
40131         }
40132         this.closeBtn.setStyle('display', this.closeBtnState);
40133         if(this.stickBtn){
40134             this.stickBtn.hide();
40135         }
40136         this.fireEvent("slidehide", this);
40137     },
40138
40139     slideIn : function(cb){
40140         if(!this.isSlid || this.el.hasActiveFx()){
40141             Roo.callback(cb);
40142             return;
40143         }
40144         this.isSlid = false;
40145         this.beforeSlide();
40146         this.el.slideOut(this.getSlideAnchor(), {
40147             callback: function(){
40148                 this.el.setLeftTop(-10000, -10000);
40149                 this.afterSlide();
40150                 this.afterSlideIn();
40151                 Roo.callback(cb);
40152             },
40153             scope: this,
40154             block: true
40155         });
40156     },
40157     
40158     slideInIf : function(e){
40159         if(!e.within(this.el)){
40160             this.slideIn();
40161         }
40162     },
40163
40164     animateCollapse : function(){
40165         this.beforeSlide();
40166         this.el.setStyle("z-index", 20000);
40167         var anchor = this.getSlideAnchor();
40168         this.el.slideOut(anchor, {
40169             callback : function(){
40170                 this.el.setStyle("z-index", "");
40171                 this.collapsedEl.slideIn(anchor, {duration:.3});
40172                 this.afterSlide();
40173                 this.el.setLocation(-10000,-10000);
40174                 this.el.hide();
40175                 this.fireEvent("collapsed", this);
40176             },
40177             scope: this,
40178             block: true
40179         });
40180     },
40181
40182     animateExpand : function(){
40183         this.beforeSlide();
40184         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40185         this.el.setStyle("z-index", 20000);
40186         this.collapsedEl.hide({
40187             duration:.1
40188         });
40189         this.el.slideIn(this.getSlideAnchor(), {
40190             callback : function(){
40191                 this.el.setStyle("z-index", "");
40192                 this.afterSlide();
40193                 if(this.split){
40194                     this.split.el.show();
40195                 }
40196                 this.fireEvent("invalidated", this);
40197                 this.fireEvent("expanded", this);
40198             },
40199             scope: this,
40200             block: true
40201         });
40202     },
40203
40204     anchors : {
40205         "west" : "left",
40206         "east" : "right",
40207         "north" : "top",
40208         "south" : "bottom"
40209     },
40210
40211     sanchors : {
40212         "west" : "l",
40213         "east" : "r",
40214         "north" : "t",
40215         "south" : "b"
40216     },
40217
40218     canchors : {
40219         "west" : "tl-tr",
40220         "east" : "tr-tl",
40221         "north" : "tl-bl",
40222         "south" : "bl-tl"
40223     },
40224
40225     getAnchor : function(){
40226         return this.anchors[this.position];
40227     },
40228
40229     getCollapseAnchor : function(){
40230         return this.canchors[this.position];
40231     },
40232
40233     getSlideAnchor : function(){
40234         return this.sanchors[this.position];
40235     },
40236
40237     getAlignAdj : function(){
40238         var cm = this.cmargins;
40239         switch(this.position){
40240             case "west":
40241                 return [0, 0];
40242             break;
40243             case "east":
40244                 return [0, 0];
40245             break;
40246             case "north":
40247                 return [0, 0];
40248             break;
40249             case "south":
40250                 return [0, 0];
40251             break;
40252         }
40253     },
40254
40255     getExpandAdj : function(){
40256         var c = this.collapsedEl, cm = this.cmargins;
40257         switch(this.position){
40258             case "west":
40259                 return [-(cm.right+c.getWidth()+cm.left), 0];
40260             break;
40261             case "east":
40262                 return [cm.right+c.getWidth()+cm.left, 0];
40263             break;
40264             case "north":
40265                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40266             break;
40267             case "south":
40268                 return [0, cm.top+cm.bottom+c.getHeight()];
40269             break;
40270         }
40271     }
40272 });/*
40273  * Based on:
40274  * Ext JS Library 1.1.1
40275  * Copyright(c) 2006-2007, Ext JS, LLC.
40276  *
40277  * Originally Released Under LGPL - original licence link has changed is not relivant.
40278  *
40279  * Fork - LGPL
40280  * <script type="text/javascript">
40281  */
40282 /*
40283  * These classes are private internal classes
40284  */
40285 Roo.bootstrap.layout.Center = function(config){
40286     config.region = "center";
40287     Roo.bootstrap.layout.Region.call(this, config);
40288     this.visible = true;
40289     this.minWidth = config.minWidth || 20;
40290     this.minHeight = config.minHeight || 20;
40291 };
40292
40293 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40294     hide : function(){
40295         // center panel can't be hidden
40296     },
40297     
40298     show : function(){
40299         // center panel can't be hidden
40300     },
40301     
40302     getMinWidth: function(){
40303         return this.minWidth;
40304     },
40305     
40306     getMinHeight: function(){
40307         return this.minHeight;
40308     }
40309 });
40310
40311
40312
40313
40314  
40315
40316
40317
40318
40319
40320
40321 Roo.bootstrap.layout.North = function(config)
40322 {
40323     config.region = 'north';
40324     config.cursor = 'n-resize';
40325     
40326     Roo.bootstrap.layout.Split.call(this, config);
40327     
40328     
40329     if(this.split){
40330         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40331         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40332         this.split.el.addClass("roo-layout-split-v");
40333     }
40334     //var size = config.initialSize || config.height;
40335     //if(this.el && typeof size != "undefined"){
40336     //    this.el.setHeight(size);
40337     //}
40338 };
40339 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40340 {
40341     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40342      
40343      
40344     onRender : function(ctr, pos)
40345     {
40346         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40347         var size = this.config.initialSize || this.config.height;
40348         if(this.el && typeof size != "undefined"){
40349             this.el.setHeight(size);
40350         }
40351     
40352     },
40353     
40354     getBox : function(){
40355         if(this.collapsed){
40356             return this.collapsedEl.getBox();
40357         }
40358         var box = this.el.getBox();
40359         if(this.split){
40360             box.height += this.split.el.getHeight();
40361         }
40362         return box;
40363     },
40364     
40365     updateBox : function(box){
40366         if(this.split && !this.collapsed){
40367             box.height -= this.split.el.getHeight();
40368             this.split.el.setLeft(box.x);
40369             this.split.el.setTop(box.y+box.height);
40370             this.split.el.setWidth(box.width);
40371         }
40372         if(this.collapsed){
40373             this.updateBody(box.width, null);
40374         }
40375         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40376     }
40377 });
40378
40379
40380
40381
40382
40383 Roo.bootstrap.layout.South = function(config){
40384     config.region = 'south';
40385     config.cursor = 's-resize';
40386     Roo.bootstrap.layout.Split.call(this, config);
40387     if(this.split){
40388         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40389         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40390         this.split.el.addClass("roo-layout-split-v");
40391     }
40392     
40393 };
40394
40395 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40396     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40397     
40398     onRender : function(ctr, pos)
40399     {
40400         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40401         var size = this.config.initialSize || this.config.height;
40402         if(this.el && typeof size != "undefined"){
40403             this.el.setHeight(size);
40404         }
40405     
40406     },
40407     
40408     getBox : function(){
40409         if(this.collapsed){
40410             return this.collapsedEl.getBox();
40411         }
40412         var box = this.el.getBox();
40413         if(this.split){
40414             var sh = this.split.el.getHeight();
40415             box.height += sh;
40416             box.y -= sh;
40417         }
40418         return box;
40419     },
40420     
40421     updateBox : function(box){
40422         if(this.split && !this.collapsed){
40423             var sh = this.split.el.getHeight();
40424             box.height -= sh;
40425             box.y += sh;
40426             this.split.el.setLeft(box.x);
40427             this.split.el.setTop(box.y-sh);
40428             this.split.el.setWidth(box.width);
40429         }
40430         if(this.collapsed){
40431             this.updateBody(box.width, null);
40432         }
40433         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40434     }
40435 });
40436
40437 Roo.bootstrap.layout.East = function(config){
40438     config.region = "east";
40439     config.cursor = "e-resize";
40440     Roo.bootstrap.layout.Split.call(this, config);
40441     if(this.split){
40442         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40443         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40444         this.split.el.addClass("roo-layout-split-h");
40445     }
40446     
40447 };
40448 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40449     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40450     
40451     onRender : function(ctr, pos)
40452     {
40453         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40454         var size = this.config.initialSize || this.config.width;
40455         if(this.el && typeof size != "undefined"){
40456             this.el.setWidth(size);
40457         }
40458     
40459     },
40460     
40461     getBox : function(){
40462         if(this.collapsed){
40463             return this.collapsedEl.getBox();
40464         }
40465         var box = this.el.getBox();
40466         if(this.split){
40467             var sw = this.split.el.getWidth();
40468             box.width += sw;
40469             box.x -= sw;
40470         }
40471         return box;
40472     },
40473
40474     updateBox : function(box){
40475         if(this.split && !this.collapsed){
40476             var sw = this.split.el.getWidth();
40477             box.width -= sw;
40478             this.split.el.setLeft(box.x);
40479             this.split.el.setTop(box.y);
40480             this.split.el.setHeight(box.height);
40481             box.x += sw;
40482         }
40483         if(this.collapsed){
40484             this.updateBody(null, box.height);
40485         }
40486         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40487     }
40488 });
40489
40490 Roo.bootstrap.layout.West = function(config){
40491     config.region = "west";
40492     config.cursor = "w-resize";
40493     
40494     Roo.bootstrap.layout.Split.call(this, config);
40495     if(this.split){
40496         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40497         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40498         this.split.el.addClass("roo-layout-split-h");
40499     }
40500     
40501 };
40502 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40503     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40504     
40505     onRender: function(ctr, pos)
40506     {
40507         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40508         var size = this.config.initialSize || this.config.width;
40509         if(typeof size != "undefined"){
40510             this.el.setWidth(size);
40511         }
40512     },
40513     
40514     getBox : function(){
40515         if(this.collapsed){
40516             return this.collapsedEl.getBox();
40517         }
40518         var box = this.el.getBox();
40519         if (box.width == 0) {
40520             box.width = this.config.width; // kludge?
40521         }
40522         if(this.split){
40523             box.width += this.split.el.getWidth();
40524         }
40525         return box;
40526     },
40527     
40528     updateBox : function(box){
40529         if(this.split && !this.collapsed){
40530             var sw = this.split.el.getWidth();
40531             box.width -= sw;
40532             this.split.el.setLeft(box.x+box.width);
40533             this.split.el.setTop(box.y);
40534             this.split.el.setHeight(box.height);
40535         }
40536         if(this.collapsed){
40537             this.updateBody(null, box.height);
40538         }
40539         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40540     }
40541 });Roo.namespace("Roo.bootstrap.panel");/*
40542  * Based on:
40543  * Ext JS Library 1.1.1
40544  * Copyright(c) 2006-2007, Ext JS, LLC.
40545  *
40546  * Originally Released Under LGPL - original licence link has changed is not relivant.
40547  *
40548  * Fork - LGPL
40549  * <script type="text/javascript">
40550  */
40551 /**
40552  * @class Roo.ContentPanel
40553  * @extends Roo.util.Observable
40554  * A basic ContentPanel element.
40555  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40556  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40557  * @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
40558  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40559  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40560  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40561  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40562  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40563  * @cfg {String} title          The title for this panel
40564  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40565  * @cfg {String} url            Calls {@link #setUrl} with this value
40566  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40567  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40568  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40569  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40570  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40571  * @cfg {Boolean} badges render the badges
40572  * @cfg {String} cls  extra classes to use  
40573  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40574
40575  * @constructor
40576  * Create a new ContentPanel.
40577  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40578  * @param {String/Object} config A string to set only the title or a config object
40579  * @param {String} content (optional) Set the HTML content for this panel
40580  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40581  */
40582 Roo.bootstrap.panel.Content = function( config){
40583     
40584     this.tpl = config.tpl || false;
40585     
40586     var el = config.el;
40587     var content = config.content;
40588
40589     if(config.autoCreate){ // xtype is available if this is called from factory
40590         el = Roo.id();
40591     }
40592     this.el = Roo.get(el);
40593     if(!this.el && config && config.autoCreate){
40594         if(typeof config.autoCreate == "object"){
40595             if(!config.autoCreate.id){
40596                 config.autoCreate.id = config.id||el;
40597             }
40598             this.el = Roo.DomHelper.append(document.body,
40599                         config.autoCreate, true);
40600         }else{
40601             var elcfg =  {
40602                 tag: "div",
40603                 cls: (config.cls || '') +
40604                     (config.background ? ' bg-' + config.background : '') +
40605                     " roo-layout-inactive-content",
40606                 id: config.id||el
40607             };
40608             if (config.iframe) {
40609                 elcfg.cn = [
40610                     {
40611                         tag : 'iframe',
40612                         style : 'border: 0px',
40613                         src : 'about:blank'
40614                     }
40615                 ];
40616             }
40617               
40618             if (config.html) {
40619                 elcfg.html = config.html;
40620                 
40621             }
40622                         
40623             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40624             if (config.iframe) {
40625                 this.iframeEl = this.el.select('iframe',true).first();
40626             }
40627             
40628         }
40629     } 
40630     this.closable = false;
40631     this.loaded = false;
40632     this.active = false;
40633    
40634       
40635     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40636         
40637         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40638         
40639         this.wrapEl = this.el; //this.el.wrap();
40640         var ti = [];
40641         if (config.toolbar.items) {
40642             ti = config.toolbar.items ;
40643             delete config.toolbar.items ;
40644         }
40645         
40646         var nitems = [];
40647         this.toolbar.render(this.wrapEl, 'before');
40648         for(var i =0;i < ti.length;i++) {
40649           //  Roo.log(['add child', items[i]]);
40650             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40651         }
40652         this.toolbar.items = nitems;
40653         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40654         delete config.toolbar;
40655         
40656     }
40657     /*
40658     // xtype created footer. - not sure if will work as we normally have to render first..
40659     if (this.footer && !this.footer.el && this.footer.xtype) {
40660         if (!this.wrapEl) {
40661             this.wrapEl = this.el.wrap();
40662         }
40663     
40664         this.footer.container = this.wrapEl.createChild();
40665          
40666         this.footer = Roo.factory(this.footer, Roo);
40667         
40668     }
40669     */
40670     
40671      if(typeof config == "string"){
40672         this.title = config;
40673     }else{
40674         Roo.apply(this, config);
40675     }
40676     
40677     if(this.resizeEl){
40678         this.resizeEl = Roo.get(this.resizeEl, true);
40679     }else{
40680         this.resizeEl = this.el;
40681     }
40682     // handle view.xtype
40683     
40684  
40685     
40686     
40687     this.addEvents({
40688         /**
40689          * @event activate
40690          * Fires when this panel is activated. 
40691          * @param {Roo.ContentPanel} this
40692          */
40693         "activate" : true,
40694         /**
40695          * @event deactivate
40696          * Fires when this panel is activated. 
40697          * @param {Roo.ContentPanel} this
40698          */
40699         "deactivate" : true,
40700
40701         /**
40702          * @event resize
40703          * Fires when this panel is resized if fitToFrame is true.
40704          * @param {Roo.ContentPanel} this
40705          * @param {Number} width The width after any component adjustments
40706          * @param {Number} height The height after any component adjustments
40707          */
40708         "resize" : true,
40709         
40710          /**
40711          * @event render
40712          * Fires when this tab is created
40713          * @param {Roo.ContentPanel} this
40714          */
40715         "render" : true,
40716         
40717           /**
40718          * @event scroll
40719          * Fires when this content is scrolled
40720          * @param {Roo.ContentPanel} this
40721          * @param {Event} scrollEvent
40722          */
40723         "scroll" : true
40724         
40725         
40726         
40727     });
40728     
40729
40730     
40731     
40732     if(this.autoScroll && !this.iframe){
40733         this.resizeEl.setStyle("overflow", "auto");
40734         this.resizeEl.on('scroll', this.onScroll, this);
40735     } else {
40736         // fix randome scrolling
40737         //this.el.on('scroll', function() {
40738         //    Roo.log('fix random scolling');
40739         //    this.scrollTo('top',0); 
40740         //});
40741     }
40742     content = content || this.content;
40743     if(content){
40744         this.setContent(content);
40745     }
40746     if(config && config.url){
40747         this.setUrl(this.url, this.params, this.loadOnce);
40748     }
40749     
40750     
40751     
40752     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40753     
40754     if (this.view && typeof(this.view.xtype) != 'undefined') {
40755         this.view.el = this.el.appendChild(document.createElement("div"));
40756         this.view = Roo.factory(this.view); 
40757         this.view.render  &&  this.view.render(false, '');  
40758     }
40759     
40760     
40761     this.fireEvent('render', this);
40762 };
40763
40764 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40765     
40766     cls : '',
40767     background : '',
40768     
40769     tabTip : '',
40770     
40771     iframe : false,
40772     iframeEl : false,
40773     
40774     /* Resize Element - use this to work out scroll etc. */
40775     resizeEl : false,
40776     
40777     setRegion : function(region){
40778         this.region = region;
40779         this.setActiveClass(region && !this.background);
40780     },
40781     
40782     
40783     setActiveClass: function(state)
40784     {
40785         if(state){
40786            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40787            this.el.setStyle('position','relative');
40788         }else{
40789            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40790            this.el.setStyle('position', 'absolute');
40791         } 
40792     },
40793     
40794     /**
40795      * Returns the toolbar for this Panel if one was configured. 
40796      * @return {Roo.Toolbar} 
40797      */
40798     getToolbar : function(){
40799         return this.toolbar;
40800     },
40801     
40802     setActiveState : function(active)
40803     {
40804         this.active = active;
40805         this.setActiveClass(active);
40806         if(!active){
40807             if(this.fireEvent("deactivate", this) === false){
40808                 return false;
40809             }
40810             return true;
40811         }
40812         this.fireEvent("activate", this);
40813         return true;
40814     },
40815     /**
40816      * Updates this panel's element (not for iframe)
40817      * @param {String} content The new content
40818      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40819     */
40820     setContent : function(content, loadScripts){
40821         if (this.iframe) {
40822             return;
40823         }
40824         
40825         this.el.update(content, loadScripts);
40826     },
40827
40828     ignoreResize : function(w, h){
40829         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40830             return true;
40831         }else{
40832             this.lastSize = {width: w, height: h};
40833             return false;
40834         }
40835     },
40836     /**
40837      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40838      * @return {Roo.UpdateManager} The UpdateManager
40839      */
40840     getUpdateManager : function(){
40841         if (this.iframe) {
40842             return false;
40843         }
40844         return this.el.getUpdateManager();
40845     },
40846      /**
40847      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40848      * Does not work with IFRAME contents
40849      * @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:
40850 <pre><code>
40851 panel.load({
40852     url: "your-url.php",
40853     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40854     callback: yourFunction,
40855     scope: yourObject, //(optional scope)
40856     discardUrl: false,
40857     nocache: false,
40858     text: "Loading...",
40859     timeout: 30,
40860     scripts: false
40861 });
40862 </code></pre>
40863      
40864      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40865      * 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.
40866      * @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}
40867      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40868      * @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.
40869      * @return {Roo.ContentPanel} this
40870      */
40871     load : function(){
40872         
40873         if (this.iframe) {
40874             return this;
40875         }
40876         
40877         var um = this.el.getUpdateManager();
40878         um.update.apply(um, arguments);
40879         return this;
40880     },
40881
40882
40883     /**
40884      * 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.
40885      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40886      * @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)
40887      * @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)
40888      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40889      */
40890     setUrl : function(url, params, loadOnce){
40891         if (this.iframe) {
40892             this.iframeEl.dom.src = url;
40893             return false;
40894         }
40895         
40896         if(this.refreshDelegate){
40897             this.removeListener("activate", this.refreshDelegate);
40898         }
40899         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40900         this.on("activate", this.refreshDelegate);
40901         return this.el.getUpdateManager();
40902     },
40903     
40904     _handleRefresh : function(url, params, loadOnce){
40905         if(!loadOnce || !this.loaded){
40906             var updater = this.el.getUpdateManager();
40907             updater.update(url, params, this._setLoaded.createDelegate(this));
40908         }
40909     },
40910     
40911     _setLoaded : function(){
40912         this.loaded = true;
40913     }, 
40914     
40915     /**
40916      * Returns this panel's id
40917      * @return {String} 
40918      */
40919     getId : function(){
40920         return this.el.id;
40921     },
40922     
40923     /** 
40924      * Returns this panel's element - used by regiosn to add.
40925      * @return {Roo.Element} 
40926      */
40927     getEl : function(){
40928         return this.wrapEl || this.el;
40929     },
40930     
40931    
40932     
40933     adjustForComponents : function(width, height)
40934     {
40935         //Roo.log('adjustForComponents ');
40936         if(this.resizeEl != this.el){
40937             width -= this.el.getFrameWidth('lr');
40938             height -= this.el.getFrameWidth('tb');
40939         }
40940         if(this.toolbar){
40941             var te = this.toolbar.getEl();
40942             te.setWidth(width);
40943             height -= te.getHeight();
40944         }
40945         if(this.footer){
40946             var te = this.footer.getEl();
40947             te.setWidth(width);
40948             height -= te.getHeight();
40949         }
40950         
40951         
40952         if(this.adjustments){
40953             width += this.adjustments[0];
40954             height += this.adjustments[1];
40955         }
40956         return {"width": width, "height": height};
40957     },
40958     
40959     setSize : function(width, height){
40960         if(this.fitToFrame && !this.ignoreResize(width, height)){
40961             if(this.fitContainer && this.resizeEl != this.el){
40962                 this.el.setSize(width, height);
40963             }
40964             var size = this.adjustForComponents(width, height);
40965             if (this.iframe) {
40966                 this.iframeEl.setSize(width,height);
40967             }
40968             
40969             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40970             this.fireEvent('resize', this, size.width, size.height);
40971             
40972             
40973         }
40974     },
40975     
40976     /**
40977      * Returns this panel's title
40978      * @return {String} 
40979      */
40980     getTitle : function(){
40981         
40982         if (typeof(this.title) != 'object') {
40983             return this.title;
40984         }
40985         
40986         var t = '';
40987         for (var k in this.title) {
40988             if (!this.title.hasOwnProperty(k)) {
40989                 continue;
40990             }
40991             
40992             if (k.indexOf('-') >= 0) {
40993                 var s = k.split('-');
40994                 for (var i = 0; i<s.length; i++) {
40995                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40996                 }
40997             } else {
40998                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40999             }
41000         }
41001         return t;
41002     },
41003     
41004     /**
41005      * Set this panel's title
41006      * @param {String} title
41007      */
41008     setTitle : function(title){
41009         this.title = title;
41010         if(this.region){
41011             this.region.updatePanelTitle(this, title);
41012         }
41013     },
41014     
41015     /**
41016      * Returns true is this panel was configured to be closable
41017      * @return {Boolean} 
41018      */
41019     isClosable : function(){
41020         return this.closable;
41021     },
41022     
41023     beforeSlide : function(){
41024         this.el.clip();
41025         this.resizeEl.clip();
41026     },
41027     
41028     afterSlide : function(){
41029         this.el.unclip();
41030         this.resizeEl.unclip();
41031     },
41032     
41033     /**
41034      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41035      *   Will fail silently if the {@link #setUrl} method has not been called.
41036      *   This does not activate the panel, just updates its content.
41037      */
41038     refresh : function(){
41039         if(this.refreshDelegate){
41040            this.loaded = false;
41041            this.refreshDelegate();
41042         }
41043     },
41044     
41045     /**
41046      * Destroys this panel
41047      */
41048     destroy : function(){
41049         this.el.removeAllListeners();
41050         var tempEl = document.createElement("span");
41051         tempEl.appendChild(this.el.dom);
41052         tempEl.innerHTML = "";
41053         this.el.remove();
41054         this.el = null;
41055     },
41056     
41057     /**
41058      * form - if the content panel contains a form - this is a reference to it.
41059      * @type {Roo.form.Form}
41060      */
41061     form : false,
41062     /**
41063      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41064      *    This contains a reference to it.
41065      * @type {Roo.View}
41066      */
41067     view : false,
41068     
41069       /**
41070      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41071      * <pre><code>
41072
41073 layout.addxtype({
41074        xtype : 'Form',
41075        items: [ .... ]
41076    }
41077 );
41078
41079 </code></pre>
41080      * @param {Object} cfg Xtype definition of item to add.
41081      */
41082     
41083     
41084     getChildContainer: function () {
41085         return this.getEl();
41086     },
41087     
41088     
41089     onScroll : function(e)
41090     {
41091         this.fireEvent('scroll', this, e);
41092     }
41093     
41094     
41095     /*
41096         var  ret = new Roo.factory(cfg);
41097         return ret;
41098         
41099         
41100         // add form..
41101         if (cfg.xtype.match(/^Form$/)) {
41102             
41103             var el;
41104             //if (this.footer) {
41105             //    el = this.footer.container.insertSibling(false, 'before');
41106             //} else {
41107                 el = this.el.createChild();
41108             //}
41109
41110             this.form = new  Roo.form.Form(cfg);
41111             
41112             
41113             if ( this.form.allItems.length) {
41114                 this.form.render(el.dom);
41115             }
41116             return this.form;
41117         }
41118         // should only have one of theses..
41119         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41120             // views.. should not be just added - used named prop 'view''
41121             
41122             cfg.el = this.el.appendChild(document.createElement("div"));
41123             // factory?
41124             
41125             var ret = new Roo.factory(cfg);
41126              
41127              ret.render && ret.render(false, ''); // render blank..
41128             this.view = ret;
41129             return ret;
41130         }
41131         return false;
41132     }
41133     \*/
41134 });
41135  
41136 /**
41137  * @class Roo.bootstrap.panel.Grid
41138  * @extends Roo.bootstrap.panel.Content
41139  * @constructor
41140  * Create a new GridPanel.
41141  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41142  * @param {Object} config A the config object
41143   
41144  */
41145
41146
41147
41148 Roo.bootstrap.panel.Grid = function(config)
41149 {
41150     
41151       
41152     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41153         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41154
41155     config.el = this.wrapper;
41156     //this.el = this.wrapper;
41157     
41158       if (config.container) {
41159         // ctor'ed from a Border/panel.grid
41160         
41161         
41162         this.wrapper.setStyle("overflow", "hidden");
41163         this.wrapper.addClass('roo-grid-container');
41164
41165     }
41166     
41167     
41168     if(config.toolbar){
41169         var tool_el = this.wrapper.createChild();    
41170         this.toolbar = Roo.factory(config.toolbar);
41171         var ti = [];
41172         if (config.toolbar.items) {
41173             ti = config.toolbar.items ;
41174             delete config.toolbar.items ;
41175         }
41176         
41177         var nitems = [];
41178         this.toolbar.render(tool_el);
41179         for(var i =0;i < ti.length;i++) {
41180           //  Roo.log(['add child', items[i]]);
41181             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41182         }
41183         this.toolbar.items = nitems;
41184         
41185         delete config.toolbar;
41186     }
41187     
41188     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41189     config.grid.scrollBody = true;;
41190     config.grid.monitorWindowResize = false; // turn off autosizing
41191     config.grid.autoHeight = false;
41192     config.grid.autoWidth = false;
41193     
41194     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41195     
41196     if (config.background) {
41197         // render grid on panel activation (if panel background)
41198         this.on('activate', function(gp) {
41199             if (!gp.grid.rendered) {
41200                 gp.grid.render(this.wrapper);
41201                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41202             }
41203         });
41204             
41205     } else {
41206         this.grid.render(this.wrapper);
41207         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41208
41209     }
41210     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41211     // ??? needed ??? config.el = this.wrapper;
41212     
41213     
41214     
41215   
41216     // xtype created footer. - not sure if will work as we normally have to render first..
41217     if (this.footer && !this.footer.el && this.footer.xtype) {
41218         
41219         var ctr = this.grid.getView().getFooterPanel(true);
41220         this.footer.dataSource = this.grid.dataSource;
41221         this.footer = Roo.factory(this.footer, Roo);
41222         this.footer.render(ctr);
41223         
41224     }
41225     
41226     
41227     
41228     
41229      
41230 };
41231
41232 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41233     getId : function(){
41234         return this.grid.id;
41235     },
41236     
41237     /**
41238      * Returns the grid for this panel
41239      * @return {Roo.bootstrap.Table} 
41240      */
41241     getGrid : function(){
41242         return this.grid;    
41243     },
41244     
41245     setSize : function(width, height){
41246         if(!this.ignoreResize(width, height)){
41247             var grid = this.grid;
41248             var size = this.adjustForComponents(width, height);
41249             // tfoot is not a footer?
41250           
41251             
41252             var gridel = grid.getGridEl();
41253             gridel.setSize(size.width, size.height);
41254             
41255             var tbd = grid.getGridEl().select('tbody', true).first();
41256             var thd = grid.getGridEl().select('thead',true).first();
41257             var tbf= grid.getGridEl().select('tfoot', true).first();
41258
41259             if (tbf) {
41260                 size.height -= tbf.getHeight();
41261             }
41262             if (thd) {
41263                 size.height -= thd.getHeight();
41264             }
41265             
41266             tbd.setSize(size.width, size.height );
41267             // this is for the account management tab -seems to work there.
41268             var thd = grid.getGridEl().select('thead',true).first();
41269             //if (tbd) {
41270             //    tbd.setSize(size.width, size.height - thd.getHeight());
41271             //}
41272              
41273             grid.autoSize();
41274         }
41275     },
41276      
41277     
41278     
41279     beforeSlide : function(){
41280         this.grid.getView().scroller.clip();
41281     },
41282     
41283     afterSlide : function(){
41284         this.grid.getView().scroller.unclip();
41285     },
41286     
41287     destroy : function(){
41288         this.grid.destroy();
41289         delete this.grid;
41290         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41291     }
41292 });
41293
41294 /**
41295  * @class Roo.bootstrap.panel.Nest
41296  * @extends Roo.bootstrap.panel.Content
41297  * @constructor
41298  * Create a new Panel, that can contain a layout.Border.
41299  * 
41300  * 
41301  * @param {Roo.BorderLayout} layout The layout for this panel
41302  * @param {String/Object} config A string to set only the title or a config object
41303  */
41304 Roo.bootstrap.panel.Nest = function(config)
41305 {
41306     // construct with only one argument..
41307     /* FIXME - implement nicer consturctors
41308     if (layout.layout) {
41309         config = layout;
41310         layout = config.layout;
41311         delete config.layout;
41312     }
41313     if (layout.xtype && !layout.getEl) {
41314         // then layout needs constructing..
41315         layout = Roo.factory(layout, Roo);
41316     }
41317     */
41318     
41319     config.el =  config.layout.getEl();
41320     
41321     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41322     
41323     config.layout.monitorWindowResize = false; // turn off autosizing
41324     this.layout = config.layout;
41325     this.layout.getEl().addClass("roo-layout-nested-layout");
41326     this.layout.parent = this;
41327     
41328     
41329     
41330     
41331 };
41332
41333 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41334
41335     setSize : function(width, height){
41336         if(!this.ignoreResize(width, height)){
41337             var size = this.adjustForComponents(width, height);
41338             var el = this.layout.getEl();
41339             if (size.height < 1) {
41340                 el.setWidth(size.width);   
41341             } else {
41342                 el.setSize(size.width, size.height);
41343             }
41344             var touch = el.dom.offsetWidth;
41345             this.layout.layout();
41346             // ie requires a double layout on the first pass
41347             if(Roo.isIE && !this.initialized){
41348                 this.initialized = true;
41349                 this.layout.layout();
41350             }
41351         }
41352     },
41353     
41354     // activate all subpanels if not currently active..
41355     
41356     setActiveState : function(active){
41357         this.active = active;
41358         this.setActiveClass(active);
41359         
41360         if(!active){
41361             this.fireEvent("deactivate", this);
41362             return;
41363         }
41364         
41365         this.fireEvent("activate", this);
41366         // not sure if this should happen before or after..
41367         if (!this.layout) {
41368             return; // should not happen..
41369         }
41370         var reg = false;
41371         for (var r in this.layout.regions) {
41372             reg = this.layout.getRegion(r);
41373             if (reg.getActivePanel()) {
41374                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41375                 reg.setActivePanel(reg.getActivePanel());
41376                 continue;
41377             }
41378             if (!reg.panels.length) {
41379                 continue;
41380             }
41381             reg.showPanel(reg.getPanel(0));
41382         }
41383         
41384         
41385         
41386         
41387     },
41388     
41389     /**
41390      * Returns the nested BorderLayout for this panel
41391      * @return {Roo.BorderLayout} 
41392      */
41393     getLayout : function(){
41394         return this.layout;
41395     },
41396     
41397      /**
41398      * Adds a xtype elements to the layout of the nested panel
41399      * <pre><code>
41400
41401 panel.addxtype({
41402        xtype : 'ContentPanel',
41403        region: 'west',
41404        items: [ .... ]
41405    }
41406 );
41407
41408 panel.addxtype({
41409         xtype : 'NestedLayoutPanel',
41410         region: 'west',
41411         layout: {
41412            center: { },
41413            west: { }   
41414         },
41415         items : [ ... list of content panels or nested layout panels.. ]
41416    }
41417 );
41418 </code></pre>
41419      * @param {Object} cfg Xtype definition of item to add.
41420      */
41421     addxtype : function(cfg) {
41422         return this.layout.addxtype(cfg);
41423     
41424     }
41425 });/*
41426  * Based on:
41427  * Ext JS Library 1.1.1
41428  * Copyright(c) 2006-2007, Ext JS, LLC.
41429  *
41430  * Originally Released Under LGPL - original licence link has changed is not relivant.
41431  *
41432  * Fork - LGPL
41433  * <script type="text/javascript">
41434  */
41435 /**
41436  * @class Roo.TabPanel
41437  * @extends Roo.util.Observable
41438  * A lightweight tab container.
41439  * <br><br>
41440  * Usage:
41441  * <pre><code>
41442 // basic tabs 1, built from existing content
41443 var tabs = new Roo.TabPanel("tabs1");
41444 tabs.addTab("script", "View Script");
41445 tabs.addTab("markup", "View Markup");
41446 tabs.activate("script");
41447
41448 // more advanced tabs, built from javascript
41449 var jtabs = new Roo.TabPanel("jtabs");
41450 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41451
41452 // set up the UpdateManager
41453 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41454 var updater = tab2.getUpdateManager();
41455 updater.setDefaultUrl("ajax1.htm");
41456 tab2.on('activate', updater.refresh, updater, true);
41457
41458 // Use setUrl for Ajax loading
41459 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41460 tab3.setUrl("ajax2.htm", null, true);
41461
41462 // Disabled tab
41463 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41464 tab4.disable();
41465
41466 jtabs.activate("jtabs-1");
41467  * </code></pre>
41468  * @constructor
41469  * Create a new TabPanel.
41470  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41471  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41472  */
41473 Roo.bootstrap.panel.Tabs = function(config){
41474     /**
41475     * The container element for this TabPanel.
41476     * @type Roo.Element
41477     */
41478     this.el = Roo.get(config.el);
41479     delete config.el;
41480     if(config){
41481         if(typeof config == "boolean"){
41482             this.tabPosition = config ? "bottom" : "top";
41483         }else{
41484             Roo.apply(this, config);
41485         }
41486     }
41487     
41488     if(this.tabPosition == "bottom"){
41489         // if tabs are at the bottom = create the body first.
41490         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41491         this.el.addClass("roo-tabs-bottom");
41492     }
41493     // next create the tabs holders
41494     
41495     if (this.tabPosition == "west"){
41496         
41497         var reg = this.region; // fake it..
41498         while (reg) {
41499             if (!reg.mgr.parent) {
41500                 break;
41501             }
41502             reg = reg.mgr.parent.region;
41503         }
41504         Roo.log("got nest?");
41505         Roo.log(reg);
41506         if (reg.mgr.getRegion('west')) {
41507             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41508             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41509             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41510             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41511             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41512         
41513             
41514         }
41515         
41516         
41517     } else {
41518      
41519         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41520         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41521         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41522         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41523     }
41524     
41525     
41526     if(Roo.isIE){
41527         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41528     }
41529     
41530     // finally - if tabs are at the top, then create the body last..
41531     if(this.tabPosition != "bottom"){
41532         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41533          * @type Roo.Element
41534          */
41535         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41536         this.el.addClass("roo-tabs-top");
41537     }
41538     this.items = [];
41539
41540     this.bodyEl.setStyle("position", "relative");
41541
41542     this.active = null;
41543     this.activateDelegate = this.activate.createDelegate(this);
41544
41545     this.addEvents({
41546         /**
41547          * @event tabchange
41548          * Fires when the active tab changes
41549          * @param {Roo.TabPanel} this
41550          * @param {Roo.TabPanelItem} activePanel The new active tab
41551          */
41552         "tabchange": true,
41553         /**
41554          * @event beforetabchange
41555          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41556          * @param {Roo.TabPanel} this
41557          * @param {Object} e Set cancel to true on this object to cancel the tab change
41558          * @param {Roo.TabPanelItem} tab The tab being changed to
41559          */
41560         "beforetabchange" : true
41561     });
41562
41563     Roo.EventManager.onWindowResize(this.onResize, this);
41564     this.cpad = this.el.getPadding("lr");
41565     this.hiddenCount = 0;
41566
41567
41568     // toolbar on the tabbar support...
41569     if (this.toolbar) {
41570         alert("no toolbar support yet");
41571         this.toolbar  = false;
41572         /*
41573         var tcfg = this.toolbar;
41574         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41575         this.toolbar = new Roo.Toolbar(tcfg);
41576         if (Roo.isSafari) {
41577             var tbl = tcfg.container.child('table', true);
41578             tbl.setAttribute('width', '100%');
41579         }
41580         */
41581         
41582     }
41583    
41584
41585
41586     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41587 };
41588
41589 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41590     /*
41591      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41592      */
41593     tabPosition : "top",
41594     /*
41595      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41596      */
41597     currentTabWidth : 0,
41598     /*
41599      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41600      */
41601     minTabWidth : 40,
41602     /*
41603      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41604      */
41605     maxTabWidth : 250,
41606     /*
41607      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41608      */
41609     preferredTabWidth : 175,
41610     /*
41611      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41612      */
41613     resizeTabs : false,
41614     /*
41615      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41616      */
41617     monitorResize : true,
41618     /*
41619      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41620      */
41621     toolbar : false,  // set by caller..
41622     
41623     region : false, /// set by caller
41624     
41625     disableTooltips : true, // not used yet...
41626
41627     /**
41628      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41629      * @param {String} id The id of the div to use <b>or create</b>
41630      * @param {String} text The text for the tab
41631      * @param {String} content (optional) Content to put in the TabPanelItem body
41632      * @param {Boolean} closable (optional) True to create a close icon on the tab
41633      * @return {Roo.TabPanelItem} The created TabPanelItem
41634      */
41635     addTab : function(id, text, content, closable, tpl)
41636     {
41637         var item = new Roo.bootstrap.panel.TabItem({
41638             panel: this,
41639             id : id,
41640             text : text,
41641             closable : closable,
41642             tpl : tpl
41643         });
41644         this.addTabItem(item);
41645         if(content){
41646             item.setContent(content);
41647         }
41648         return item;
41649     },
41650
41651     /**
41652      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41653      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41654      * @return {Roo.TabPanelItem}
41655      */
41656     getTab : function(id){
41657         return this.items[id];
41658     },
41659
41660     /**
41661      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41662      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41663      */
41664     hideTab : function(id){
41665         var t = this.items[id];
41666         if(!t.isHidden()){
41667            t.setHidden(true);
41668            this.hiddenCount++;
41669            this.autoSizeTabs();
41670         }
41671     },
41672
41673     /**
41674      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41675      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41676      */
41677     unhideTab : function(id){
41678         var t = this.items[id];
41679         if(t.isHidden()){
41680            t.setHidden(false);
41681            this.hiddenCount--;
41682            this.autoSizeTabs();
41683         }
41684     },
41685
41686     /**
41687      * Adds an existing {@link Roo.TabPanelItem}.
41688      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41689      */
41690     addTabItem : function(item)
41691     {
41692         this.items[item.id] = item;
41693         this.items.push(item);
41694         this.autoSizeTabs();
41695       //  if(this.resizeTabs){
41696     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41697   //         this.autoSizeTabs();
41698 //        }else{
41699 //            item.autoSize();
41700        // }
41701     },
41702
41703     /**
41704      * Removes a {@link Roo.TabPanelItem}.
41705      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41706      */
41707     removeTab : function(id){
41708         var items = this.items;
41709         var tab = items[id];
41710         if(!tab) { return; }
41711         var index = items.indexOf(tab);
41712         if(this.active == tab && items.length > 1){
41713             var newTab = this.getNextAvailable(index);
41714             if(newTab) {
41715                 newTab.activate();
41716             }
41717         }
41718         this.stripEl.dom.removeChild(tab.pnode.dom);
41719         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41720             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41721         }
41722         items.splice(index, 1);
41723         delete this.items[tab.id];
41724         tab.fireEvent("close", tab);
41725         tab.purgeListeners();
41726         this.autoSizeTabs();
41727     },
41728
41729     getNextAvailable : function(start){
41730         var items = this.items;
41731         var index = start;
41732         // look for a next tab that will slide over to
41733         // replace the one being removed
41734         while(index < items.length){
41735             var item = items[++index];
41736             if(item && !item.isHidden()){
41737                 return item;
41738             }
41739         }
41740         // if one isn't found select the previous tab (on the left)
41741         index = start;
41742         while(index >= 0){
41743             var item = items[--index];
41744             if(item && !item.isHidden()){
41745                 return item;
41746             }
41747         }
41748         return null;
41749     },
41750
41751     /**
41752      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41753      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41754      */
41755     disableTab : function(id){
41756         var tab = this.items[id];
41757         if(tab && this.active != tab){
41758             tab.disable();
41759         }
41760     },
41761
41762     /**
41763      * Enables a {@link Roo.TabPanelItem} that is disabled.
41764      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41765      */
41766     enableTab : function(id){
41767         var tab = this.items[id];
41768         tab.enable();
41769     },
41770
41771     /**
41772      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41773      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41774      * @return {Roo.TabPanelItem} The TabPanelItem.
41775      */
41776     activate : function(id)
41777     {
41778         //Roo.log('activite:'  + id);
41779         
41780         var tab = this.items[id];
41781         if(!tab){
41782             return null;
41783         }
41784         if(tab == this.active || tab.disabled){
41785             return tab;
41786         }
41787         var e = {};
41788         this.fireEvent("beforetabchange", this, e, tab);
41789         if(e.cancel !== true && !tab.disabled){
41790             if(this.active){
41791                 this.active.hide();
41792             }
41793             this.active = this.items[id];
41794             this.active.show();
41795             this.fireEvent("tabchange", this, this.active);
41796         }
41797         return tab;
41798     },
41799
41800     /**
41801      * Gets the active {@link Roo.TabPanelItem}.
41802      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41803      */
41804     getActiveTab : function(){
41805         return this.active;
41806     },
41807
41808     /**
41809      * Updates the tab body element to fit the height of the container element
41810      * for overflow scrolling
41811      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41812      */
41813     syncHeight : function(targetHeight){
41814         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41815         var bm = this.bodyEl.getMargins();
41816         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41817         this.bodyEl.setHeight(newHeight);
41818         return newHeight;
41819     },
41820
41821     onResize : function(){
41822         if(this.monitorResize){
41823             this.autoSizeTabs();
41824         }
41825     },
41826
41827     /**
41828      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41829      */
41830     beginUpdate : function(){
41831         this.updating = true;
41832     },
41833
41834     /**
41835      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41836      */
41837     endUpdate : function(){
41838         this.updating = false;
41839         this.autoSizeTabs();
41840     },
41841
41842     /**
41843      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41844      */
41845     autoSizeTabs : function()
41846     {
41847         var count = this.items.length;
41848         var vcount = count - this.hiddenCount;
41849         
41850         if (vcount < 2) {
41851             this.stripEl.hide();
41852         } else {
41853             this.stripEl.show();
41854         }
41855         
41856         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41857             return;
41858         }
41859         
41860         
41861         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41862         var availWidth = Math.floor(w / vcount);
41863         var b = this.stripBody;
41864         if(b.getWidth() > w){
41865             var tabs = this.items;
41866             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41867             if(availWidth < this.minTabWidth){
41868                 /*if(!this.sleft){    // incomplete scrolling code
41869                     this.createScrollButtons();
41870                 }
41871                 this.showScroll();
41872                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41873             }
41874         }else{
41875             if(this.currentTabWidth < this.preferredTabWidth){
41876                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41877             }
41878         }
41879     },
41880
41881     /**
41882      * Returns the number of tabs in this TabPanel.
41883      * @return {Number}
41884      */
41885      getCount : function(){
41886          return this.items.length;
41887      },
41888
41889     /**
41890      * Resizes all the tabs to the passed width
41891      * @param {Number} The new width
41892      */
41893     setTabWidth : function(width){
41894         this.currentTabWidth = width;
41895         for(var i = 0, len = this.items.length; i < len; i++) {
41896                 if(!this.items[i].isHidden()) {
41897                 this.items[i].setWidth(width);
41898             }
41899         }
41900     },
41901
41902     /**
41903      * Destroys this TabPanel
41904      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41905      */
41906     destroy : function(removeEl){
41907         Roo.EventManager.removeResizeListener(this.onResize, this);
41908         for(var i = 0, len = this.items.length; i < len; i++){
41909             this.items[i].purgeListeners();
41910         }
41911         if(removeEl === true){
41912             this.el.update("");
41913             this.el.remove();
41914         }
41915     },
41916     
41917     createStrip : function(container)
41918     {
41919         var strip = document.createElement("nav");
41920         strip.className = Roo.bootstrap.version == 4 ?
41921             "navbar-light bg-light" : 
41922             "navbar navbar-default"; //"x-tabs-wrap";
41923         container.appendChild(strip);
41924         return strip;
41925     },
41926     
41927     createStripList : function(strip)
41928     {
41929         // div wrapper for retard IE
41930         // returns the "tr" element.
41931         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41932         //'<div class="x-tabs-strip-wrap">'+
41933           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41934           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41935         return strip.firstChild; //.firstChild.firstChild.firstChild;
41936     },
41937     createBody : function(container)
41938     {
41939         var body = document.createElement("div");
41940         Roo.id(body, "tab-body");
41941         //Roo.fly(body).addClass("x-tabs-body");
41942         Roo.fly(body).addClass("tab-content");
41943         container.appendChild(body);
41944         return body;
41945     },
41946     createItemBody :function(bodyEl, id){
41947         var body = Roo.getDom(id);
41948         if(!body){
41949             body = document.createElement("div");
41950             body.id = id;
41951         }
41952         //Roo.fly(body).addClass("x-tabs-item-body");
41953         Roo.fly(body).addClass("tab-pane");
41954          bodyEl.insertBefore(body, bodyEl.firstChild);
41955         return body;
41956     },
41957     /** @private */
41958     createStripElements :  function(stripEl, text, closable, tpl)
41959     {
41960         var td = document.createElement("li"); // was td..
41961         td.className = 'nav-item';
41962         
41963         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41964         
41965         
41966         stripEl.appendChild(td);
41967         /*if(closable){
41968             td.className = "x-tabs-closable";
41969             if(!this.closeTpl){
41970                 this.closeTpl = new Roo.Template(
41971                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41972                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41973                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41974                 );
41975             }
41976             var el = this.closeTpl.overwrite(td, {"text": text});
41977             var close = el.getElementsByTagName("div")[0];
41978             var inner = el.getElementsByTagName("em")[0];
41979             return {"el": el, "close": close, "inner": inner};
41980         } else {
41981         */
41982         // not sure what this is..
41983 //            if(!this.tabTpl){
41984                 //this.tabTpl = new Roo.Template(
41985                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41986                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41987                 //);
41988 //                this.tabTpl = new Roo.Template(
41989 //                   '<a href="#">' +
41990 //                   '<span unselectable="on"' +
41991 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41992 //                            ' >{text}</span></a>'
41993 //                );
41994 //                
41995 //            }
41996
41997
41998             var template = tpl || this.tabTpl || false;
41999             
42000             if(!template){
42001                 template =  new Roo.Template(
42002                         Roo.bootstrap.version == 4 ? 
42003                             (
42004                                 '<a class="nav-link" href="#" unselectable="on"' +
42005                                      (this.disableTooltips ? '' : ' title="{text}"') +
42006                                      ' >{text}</a>'
42007                             ) : (
42008                                 '<a class="nav-link" href="#">' +
42009                                 '<span unselectable="on"' +
42010                                          (this.disableTooltips ? '' : ' title="{text}"') +
42011                                     ' >{text}</span></a>'
42012                             )
42013                 );
42014             }
42015             
42016             switch (typeof(template)) {
42017                 case 'object' :
42018                     break;
42019                 case 'string' :
42020                     template = new Roo.Template(template);
42021                     break;
42022                 default :
42023                     break;
42024             }
42025             
42026             var el = template.overwrite(td, {"text": text});
42027             
42028             var inner = el.getElementsByTagName("span")[0];
42029             
42030             return {"el": el, "inner": inner};
42031             
42032     }
42033         
42034     
42035 });
42036
42037 /**
42038  * @class Roo.TabPanelItem
42039  * @extends Roo.util.Observable
42040  * Represents an individual item (tab plus body) in a TabPanel.
42041  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42042  * @param {String} id The id of this TabPanelItem
42043  * @param {String} text The text for the tab of this TabPanelItem
42044  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42045  */
42046 Roo.bootstrap.panel.TabItem = function(config){
42047     /**
42048      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42049      * @type Roo.TabPanel
42050      */
42051     this.tabPanel = config.panel;
42052     /**
42053      * The id for this TabPanelItem
42054      * @type String
42055      */
42056     this.id = config.id;
42057     /** @private */
42058     this.disabled = false;
42059     /** @private */
42060     this.text = config.text;
42061     /** @private */
42062     this.loaded = false;
42063     this.closable = config.closable;
42064
42065     /**
42066      * The body element for this TabPanelItem.
42067      * @type Roo.Element
42068      */
42069     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42070     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42071     this.bodyEl.setStyle("display", "block");
42072     this.bodyEl.setStyle("zoom", "1");
42073     //this.hideAction();
42074
42075     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42076     /** @private */
42077     this.el = Roo.get(els.el);
42078     this.inner = Roo.get(els.inner, true);
42079      this.textEl = Roo.bootstrap.version == 4 ?
42080         this.el : Roo.get(this.el.dom.firstChild, true);
42081
42082     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42083     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42084
42085     
42086 //    this.el.on("mousedown", this.onTabMouseDown, this);
42087     this.el.on("click", this.onTabClick, this);
42088     /** @private */
42089     if(config.closable){
42090         var c = Roo.get(els.close, true);
42091         c.dom.title = this.closeText;
42092         c.addClassOnOver("close-over");
42093         c.on("click", this.closeClick, this);
42094      }
42095
42096     this.addEvents({
42097          /**
42098          * @event activate
42099          * Fires when this tab becomes the active tab.
42100          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42101          * @param {Roo.TabPanelItem} this
42102          */
42103         "activate": true,
42104         /**
42105          * @event beforeclose
42106          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42107          * @param {Roo.TabPanelItem} this
42108          * @param {Object} e Set cancel to true on this object to cancel the close.
42109          */
42110         "beforeclose": true,
42111         /**
42112          * @event close
42113          * Fires when this tab is closed.
42114          * @param {Roo.TabPanelItem} this
42115          */
42116          "close": true,
42117         /**
42118          * @event deactivate
42119          * Fires when this tab is no longer the active tab.
42120          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42121          * @param {Roo.TabPanelItem} this
42122          */
42123          "deactivate" : true
42124     });
42125     this.hidden = false;
42126
42127     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42128 };
42129
42130 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42131            {
42132     purgeListeners : function(){
42133        Roo.util.Observable.prototype.purgeListeners.call(this);
42134        this.el.removeAllListeners();
42135     },
42136     /**
42137      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42138      */
42139     show : function(){
42140         this.status_node.addClass("active");
42141         this.showAction();
42142         if(Roo.isOpera){
42143             this.tabPanel.stripWrap.repaint();
42144         }
42145         this.fireEvent("activate", this.tabPanel, this);
42146     },
42147
42148     /**
42149      * Returns true if this tab is the active tab.
42150      * @return {Boolean}
42151      */
42152     isActive : function(){
42153         return this.tabPanel.getActiveTab() == this;
42154     },
42155
42156     /**
42157      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42158      */
42159     hide : function(){
42160         this.status_node.removeClass("active");
42161         this.hideAction();
42162         this.fireEvent("deactivate", this.tabPanel, this);
42163     },
42164
42165     hideAction : function(){
42166         this.bodyEl.hide();
42167         this.bodyEl.setStyle("position", "absolute");
42168         this.bodyEl.setLeft("-20000px");
42169         this.bodyEl.setTop("-20000px");
42170     },
42171
42172     showAction : function(){
42173         this.bodyEl.setStyle("position", "relative");
42174         this.bodyEl.setTop("");
42175         this.bodyEl.setLeft("");
42176         this.bodyEl.show();
42177     },
42178
42179     /**
42180      * Set the tooltip for the tab.
42181      * @param {String} tooltip The tab's tooltip
42182      */
42183     setTooltip : function(text){
42184         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42185             this.textEl.dom.qtip = text;
42186             this.textEl.dom.removeAttribute('title');
42187         }else{
42188             this.textEl.dom.title = text;
42189         }
42190     },
42191
42192     onTabClick : function(e){
42193         e.preventDefault();
42194         this.tabPanel.activate(this.id);
42195     },
42196
42197     onTabMouseDown : function(e){
42198         e.preventDefault();
42199         this.tabPanel.activate(this.id);
42200     },
42201 /*
42202     getWidth : function(){
42203         return this.inner.getWidth();
42204     },
42205
42206     setWidth : function(width){
42207         var iwidth = width - this.linode.getPadding("lr");
42208         this.inner.setWidth(iwidth);
42209         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42210         this.linode.setWidth(width);
42211     },
42212 */
42213     /**
42214      * Show or hide the tab
42215      * @param {Boolean} hidden True to hide or false to show.
42216      */
42217     setHidden : function(hidden){
42218         this.hidden = hidden;
42219         this.linode.setStyle("display", hidden ? "none" : "");
42220     },
42221
42222     /**
42223      * Returns true if this tab is "hidden"
42224      * @return {Boolean}
42225      */
42226     isHidden : function(){
42227         return this.hidden;
42228     },
42229
42230     /**
42231      * Returns the text for this tab
42232      * @return {String}
42233      */
42234     getText : function(){
42235         return this.text;
42236     },
42237     /*
42238     autoSize : function(){
42239         //this.el.beginMeasure();
42240         this.textEl.setWidth(1);
42241         /*
42242          *  #2804 [new] Tabs in Roojs
42243          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42244          */
42245         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42246         //this.el.endMeasure();
42247     //},
42248
42249     /**
42250      * Sets the text for the tab (Note: this also sets the tooltip text)
42251      * @param {String} text The tab's text and tooltip
42252      */
42253     setText : function(text){
42254         this.text = text;
42255         this.textEl.update(text);
42256         this.setTooltip(text);
42257         //if(!this.tabPanel.resizeTabs){
42258         //    this.autoSize();
42259         //}
42260     },
42261     /**
42262      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42263      */
42264     activate : function(){
42265         this.tabPanel.activate(this.id);
42266     },
42267
42268     /**
42269      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42270      */
42271     disable : function(){
42272         if(this.tabPanel.active != this){
42273             this.disabled = true;
42274             this.status_node.addClass("disabled");
42275         }
42276     },
42277
42278     /**
42279      * Enables this TabPanelItem if it was previously disabled.
42280      */
42281     enable : function(){
42282         this.disabled = false;
42283         this.status_node.removeClass("disabled");
42284     },
42285
42286     /**
42287      * Sets the content for this TabPanelItem.
42288      * @param {String} content The content
42289      * @param {Boolean} loadScripts true to look for and load scripts
42290      */
42291     setContent : function(content, loadScripts){
42292         this.bodyEl.update(content, loadScripts);
42293     },
42294
42295     /**
42296      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42297      * @return {Roo.UpdateManager} The UpdateManager
42298      */
42299     getUpdateManager : function(){
42300         return this.bodyEl.getUpdateManager();
42301     },
42302
42303     /**
42304      * Set a URL to be used to load the content for this TabPanelItem.
42305      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42306      * @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)
42307      * @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)
42308      * @return {Roo.UpdateManager} The UpdateManager
42309      */
42310     setUrl : function(url, params, loadOnce){
42311         if(this.refreshDelegate){
42312             this.un('activate', this.refreshDelegate);
42313         }
42314         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42315         this.on("activate", this.refreshDelegate);
42316         return this.bodyEl.getUpdateManager();
42317     },
42318
42319     /** @private */
42320     _handleRefresh : function(url, params, loadOnce){
42321         if(!loadOnce || !this.loaded){
42322             var updater = this.bodyEl.getUpdateManager();
42323             updater.update(url, params, this._setLoaded.createDelegate(this));
42324         }
42325     },
42326
42327     /**
42328      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42329      *   Will fail silently if the setUrl method has not been called.
42330      *   This does not activate the panel, just updates its content.
42331      */
42332     refresh : function(){
42333         if(this.refreshDelegate){
42334            this.loaded = false;
42335            this.refreshDelegate();
42336         }
42337     },
42338
42339     /** @private */
42340     _setLoaded : function(){
42341         this.loaded = true;
42342     },
42343
42344     /** @private */
42345     closeClick : function(e){
42346         var o = {};
42347         e.stopEvent();
42348         this.fireEvent("beforeclose", this, o);
42349         if(o.cancel !== true){
42350             this.tabPanel.removeTab(this.id);
42351         }
42352     },
42353     /**
42354      * The text displayed in the tooltip for the close icon.
42355      * @type String
42356      */
42357     closeText : "Close this tab"
42358 });
42359 /**
42360 *    This script refer to:
42361 *    Title: International Telephone Input
42362 *    Author: Jack O'Connor
42363 *    Code version:  v12.1.12
42364 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42365 **/
42366
42367 Roo.bootstrap.PhoneInputData = function() {
42368     var d = [
42369       [
42370         "Afghanistan (‫افغانستان‬‎)",
42371         "af",
42372         "93"
42373       ],
42374       [
42375         "Albania (Shqipëri)",
42376         "al",
42377         "355"
42378       ],
42379       [
42380         "Algeria (‫الجزائر‬‎)",
42381         "dz",
42382         "213"
42383       ],
42384       [
42385         "American Samoa",
42386         "as",
42387         "1684"
42388       ],
42389       [
42390         "Andorra",
42391         "ad",
42392         "376"
42393       ],
42394       [
42395         "Angola",
42396         "ao",
42397         "244"
42398       ],
42399       [
42400         "Anguilla",
42401         "ai",
42402         "1264"
42403       ],
42404       [
42405         "Antigua and Barbuda",
42406         "ag",
42407         "1268"
42408       ],
42409       [
42410         "Argentina",
42411         "ar",
42412         "54"
42413       ],
42414       [
42415         "Armenia (Հայաստան)",
42416         "am",
42417         "374"
42418       ],
42419       [
42420         "Aruba",
42421         "aw",
42422         "297"
42423       ],
42424       [
42425         "Australia",
42426         "au",
42427         "61",
42428         0
42429       ],
42430       [
42431         "Austria (Österreich)",
42432         "at",
42433         "43"
42434       ],
42435       [
42436         "Azerbaijan (Azərbaycan)",
42437         "az",
42438         "994"
42439       ],
42440       [
42441         "Bahamas",
42442         "bs",
42443         "1242"
42444       ],
42445       [
42446         "Bahrain (‫البحرين‬‎)",
42447         "bh",
42448         "973"
42449       ],
42450       [
42451         "Bangladesh (বাংলাদেশ)",
42452         "bd",
42453         "880"
42454       ],
42455       [
42456         "Barbados",
42457         "bb",
42458         "1246"
42459       ],
42460       [
42461         "Belarus (Беларусь)",
42462         "by",
42463         "375"
42464       ],
42465       [
42466         "Belgium (België)",
42467         "be",
42468         "32"
42469       ],
42470       [
42471         "Belize",
42472         "bz",
42473         "501"
42474       ],
42475       [
42476         "Benin (Bénin)",
42477         "bj",
42478         "229"
42479       ],
42480       [
42481         "Bermuda",
42482         "bm",
42483         "1441"
42484       ],
42485       [
42486         "Bhutan (འབྲུག)",
42487         "bt",
42488         "975"
42489       ],
42490       [
42491         "Bolivia",
42492         "bo",
42493         "591"
42494       ],
42495       [
42496         "Bosnia and Herzegovina (Босна и Херцеговина)",
42497         "ba",
42498         "387"
42499       ],
42500       [
42501         "Botswana",
42502         "bw",
42503         "267"
42504       ],
42505       [
42506         "Brazil (Brasil)",
42507         "br",
42508         "55"
42509       ],
42510       [
42511         "British Indian Ocean Territory",
42512         "io",
42513         "246"
42514       ],
42515       [
42516         "British Virgin Islands",
42517         "vg",
42518         "1284"
42519       ],
42520       [
42521         "Brunei",
42522         "bn",
42523         "673"
42524       ],
42525       [
42526         "Bulgaria (България)",
42527         "bg",
42528         "359"
42529       ],
42530       [
42531         "Burkina Faso",
42532         "bf",
42533         "226"
42534       ],
42535       [
42536         "Burundi (Uburundi)",
42537         "bi",
42538         "257"
42539       ],
42540       [
42541         "Cambodia (កម្ពុជា)",
42542         "kh",
42543         "855"
42544       ],
42545       [
42546         "Cameroon (Cameroun)",
42547         "cm",
42548         "237"
42549       ],
42550       [
42551         "Canada",
42552         "ca",
42553         "1",
42554         1,
42555         ["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"]
42556       ],
42557       [
42558         "Cape Verde (Kabu Verdi)",
42559         "cv",
42560         "238"
42561       ],
42562       [
42563         "Caribbean Netherlands",
42564         "bq",
42565         "599",
42566         1
42567       ],
42568       [
42569         "Cayman Islands",
42570         "ky",
42571         "1345"
42572       ],
42573       [
42574         "Central African Republic (République centrafricaine)",
42575         "cf",
42576         "236"
42577       ],
42578       [
42579         "Chad (Tchad)",
42580         "td",
42581         "235"
42582       ],
42583       [
42584         "Chile",
42585         "cl",
42586         "56"
42587       ],
42588       [
42589         "China (中国)",
42590         "cn",
42591         "86"
42592       ],
42593       [
42594         "Christmas Island",
42595         "cx",
42596         "61",
42597         2
42598       ],
42599       [
42600         "Cocos (Keeling) Islands",
42601         "cc",
42602         "61",
42603         1
42604       ],
42605       [
42606         "Colombia",
42607         "co",
42608         "57"
42609       ],
42610       [
42611         "Comoros (‫جزر القمر‬‎)",
42612         "km",
42613         "269"
42614       ],
42615       [
42616         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42617         "cd",
42618         "243"
42619       ],
42620       [
42621         "Congo (Republic) (Congo-Brazzaville)",
42622         "cg",
42623         "242"
42624       ],
42625       [
42626         "Cook Islands",
42627         "ck",
42628         "682"
42629       ],
42630       [
42631         "Costa Rica",
42632         "cr",
42633         "506"
42634       ],
42635       [
42636         "Côte d’Ivoire",
42637         "ci",
42638         "225"
42639       ],
42640       [
42641         "Croatia (Hrvatska)",
42642         "hr",
42643         "385"
42644       ],
42645       [
42646         "Cuba",
42647         "cu",
42648         "53"
42649       ],
42650       [
42651         "Curaçao",
42652         "cw",
42653         "599",
42654         0
42655       ],
42656       [
42657         "Cyprus (Κύπρος)",
42658         "cy",
42659         "357"
42660       ],
42661       [
42662         "Czech Republic (Česká republika)",
42663         "cz",
42664         "420"
42665       ],
42666       [
42667         "Denmark (Danmark)",
42668         "dk",
42669         "45"
42670       ],
42671       [
42672         "Djibouti",
42673         "dj",
42674         "253"
42675       ],
42676       [
42677         "Dominica",
42678         "dm",
42679         "1767"
42680       ],
42681       [
42682         "Dominican Republic (República Dominicana)",
42683         "do",
42684         "1",
42685         2,
42686         ["809", "829", "849"]
42687       ],
42688       [
42689         "Ecuador",
42690         "ec",
42691         "593"
42692       ],
42693       [
42694         "Egypt (‫مصر‬‎)",
42695         "eg",
42696         "20"
42697       ],
42698       [
42699         "El Salvador",
42700         "sv",
42701         "503"
42702       ],
42703       [
42704         "Equatorial Guinea (Guinea Ecuatorial)",
42705         "gq",
42706         "240"
42707       ],
42708       [
42709         "Eritrea",
42710         "er",
42711         "291"
42712       ],
42713       [
42714         "Estonia (Eesti)",
42715         "ee",
42716         "372"
42717       ],
42718       [
42719         "Ethiopia",
42720         "et",
42721         "251"
42722       ],
42723       [
42724         "Falkland Islands (Islas Malvinas)",
42725         "fk",
42726         "500"
42727       ],
42728       [
42729         "Faroe Islands (Føroyar)",
42730         "fo",
42731         "298"
42732       ],
42733       [
42734         "Fiji",
42735         "fj",
42736         "679"
42737       ],
42738       [
42739         "Finland (Suomi)",
42740         "fi",
42741         "358",
42742         0
42743       ],
42744       [
42745         "France",
42746         "fr",
42747         "33"
42748       ],
42749       [
42750         "French Guiana (Guyane française)",
42751         "gf",
42752         "594"
42753       ],
42754       [
42755         "French Polynesia (Polynésie française)",
42756         "pf",
42757         "689"
42758       ],
42759       [
42760         "Gabon",
42761         "ga",
42762         "241"
42763       ],
42764       [
42765         "Gambia",
42766         "gm",
42767         "220"
42768       ],
42769       [
42770         "Georgia (საქართველო)",
42771         "ge",
42772         "995"
42773       ],
42774       [
42775         "Germany (Deutschland)",
42776         "de",
42777         "49"
42778       ],
42779       [
42780         "Ghana (Gaana)",
42781         "gh",
42782         "233"
42783       ],
42784       [
42785         "Gibraltar",
42786         "gi",
42787         "350"
42788       ],
42789       [
42790         "Greece (Ελλάδα)",
42791         "gr",
42792         "30"
42793       ],
42794       [
42795         "Greenland (Kalaallit Nunaat)",
42796         "gl",
42797         "299"
42798       ],
42799       [
42800         "Grenada",
42801         "gd",
42802         "1473"
42803       ],
42804       [
42805         "Guadeloupe",
42806         "gp",
42807         "590",
42808         0
42809       ],
42810       [
42811         "Guam",
42812         "gu",
42813         "1671"
42814       ],
42815       [
42816         "Guatemala",
42817         "gt",
42818         "502"
42819       ],
42820       [
42821         "Guernsey",
42822         "gg",
42823         "44",
42824         1
42825       ],
42826       [
42827         "Guinea (Guinée)",
42828         "gn",
42829         "224"
42830       ],
42831       [
42832         "Guinea-Bissau (Guiné Bissau)",
42833         "gw",
42834         "245"
42835       ],
42836       [
42837         "Guyana",
42838         "gy",
42839         "592"
42840       ],
42841       [
42842         "Haiti",
42843         "ht",
42844         "509"
42845       ],
42846       [
42847         "Honduras",
42848         "hn",
42849         "504"
42850       ],
42851       [
42852         "Hong Kong (香港)",
42853         "hk",
42854         "852"
42855       ],
42856       [
42857         "Hungary (Magyarország)",
42858         "hu",
42859         "36"
42860       ],
42861       [
42862         "Iceland (Ísland)",
42863         "is",
42864         "354"
42865       ],
42866       [
42867         "India (भारत)",
42868         "in",
42869         "91"
42870       ],
42871       [
42872         "Indonesia",
42873         "id",
42874         "62"
42875       ],
42876       [
42877         "Iran (‫ایران‬‎)",
42878         "ir",
42879         "98"
42880       ],
42881       [
42882         "Iraq (‫العراق‬‎)",
42883         "iq",
42884         "964"
42885       ],
42886       [
42887         "Ireland",
42888         "ie",
42889         "353"
42890       ],
42891       [
42892         "Isle of Man",
42893         "im",
42894         "44",
42895         2
42896       ],
42897       [
42898         "Israel (‫ישראל‬‎)",
42899         "il",
42900         "972"
42901       ],
42902       [
42903         "Italy (Italia)",
42904         "it",
42905         "39",
42906         0
42907       ],
42908       [
42909         "Jamaica",
42910         "jm",
42911         "1876"
42912       ],
42913       [
42914         "Japan (日本)",
42915         "jp",
42916         "81"
42917       ],
42918       [
42919         "Jersey",
42920         "je",
42921         "44",
42922         3
42923       ],
42924       [
42925         "Jordan (‫الأردن‬‎)",
42926         "jo",
42927         "962"
42928       ],
42929       [
42930         "Kazakhstan (Казахстан)",
42931         "kz",
42932         "7",
42933         1
42934       ],
42935       [
42936         "Kenya",
42937         "ke",
42938         "254"
42939       ],
42940       [
42941         "Kiribati",
42942         "ki",
42943         "686"
42944       ],
42945       [
42946         "Kosovo",
42947         "xk",
42948         "383"
42949       ],
42950       [
42951         "Kuwait (‫الكويت‬‎)",
42952         "kw",
42953         "965"
42954       ],
42955       [
42956         "Kyrgyzstan (Кыргызстан)",
42957         "kg",
42958         "996"
42959       ],
42960       [
42961         "Laos (ລາວ)",
42962         "la",
42963         "856"
42964       ],
42965       [
42966         "Latvia (Latvija)",
42967         "lv",
42968         "371"
42969       ],
42970       [
42971         "Lebanon (‫لبنان‬‎)",
42972         "lb",
42973         "961"
42974       ],
42975       [
42976         "Lesotho",
42977         "ls",
42978         "266"
42979       ],
42980       [
42981         "Liberia",
42982         "lr",
42983         "231"
42984       ],
42985       [
42986         "Libya (‫ليبيا‬‎)",
42987         "ly",
42988         "218"
42989       ],
42990       [
42991         "Liechtenstein",
42992         "li",
42993         "423"
42994       ],
42995       [
42996         "Lithuania (Lietuva)",
42997         "lt",
42998         "370"
42999       ],
43000       [
43001         "Luxembourg",
43002         "lu",
43003         "352"
43004       ],
43005       [
43006         "Macau (澳門)",
43007         "mo",
43008         "853"
43009       ],
43010       [
43011         "Macedonia (FYROM) (Македонија)",
43012         "mk",
43013         "389"
43014       ],
43015       [
43016         "Madagascar (Madagasikara)",
43017         "mg",
43018         "261"
43019       ],
43020       [
43021         "Malawi",
43022         "mw",
43023         "265"
43024       ],
43025       [
43026         "Malaysia",
43027         "my",
43028         "60"
43029       ],
43030       [
43031         "Maldives",
43032         "mv",
43033         "960"
43034       ],
43035       [
43036         "Mali",
43037         "ml",
43038         "223"
43039       ],
43040       [
43041         "Malta",
43042         "mt",
43043         "356"
43044       ],
43045       [
43046         "Marshall Islands",
43047         "mh",
43048         "692"
43049       ],
43050       [
43051         "Martinique",
43052         "mq",
43053         "596"
43054       ],
43055       [
43056         "Mauritania (‫موريتانيا‬‎)",
43057         "mr",
43058         "222"
43059       ],
43060       [
43061         "Mauritius (Moris)",
43062         "mu",
43063         "230"
43064       ],
43065       [
43066         "Mayotte",
43067         "yt",
43068         "262",
43069         1
43070       ],
43071       [
43072         "Mexico (México)",
43073         "mx",
43074         "52"
43075       ],
43076       [
43077         "Micronesia",
43078         "fm",
43079         "691"
43080       ],
43081       [
43082         "Moldova (Republica Moldova)",
43083         "md",
43084         "373"
43085       ],
43086       [
43087         "Monaco",
43088         "mc",
43089         "377"
43090       ],
43091       [
43092         "Mongolia (Монгол)",
43093         "mn",
43094         "976"
43095       ],
43096       [
43097         "Montenegro (Crna Gora)",
43098         "me",
43099         "382"
43100       ],
43101       [
43102         "Montserrat",
43103         "ms",
43104         "1664"
43105       ],
43106       [
43107         "Morocco (‫المغرب‬‎)",
43108         "ma",
43109         "212",
43110         0
43111       ],
43112       [
43113         "Mozambique (Moçambique)",
43114         "mz",
43115         "258"
43116       ],
43117       [
43118         "Myanmar (Burma) (မြန်မာ)",
43119         "mm",
43120         "95"
43121       ],
43122       [
43123         "Namibia (Namibië)",
43124         "na",
43125         "264"
43126       ],
43127       [
43128         "Nauru",
43129         "nr",
43130         "674"
43131       ],
43132       [
43133         "Nepal (नेपाल)",
43134         "np",
43135         "977"
43136       ],
43137       [
43138         "Netherlands (Nederland)",
43139         "nl",
43140         "31"
43141       ],
43142       [
43143         "New Caledonia (Nouvelle-Calédonie)",
43144         "nc",
43145         "687"
43146       ],
43147       [
43148         "New Zealand",
43149         "nz",
43150         "64"
43151       ],
43152       [
43153         "Nicaragua",
43154         "ni",
43155         "505"
43156       ],
43157       [
43158         "Niger (Nijar)",
43159         "ne",
43160         "227"
43161       ],
43162       [
43163         "Nigeria",
43164         "ng",
43165         "234"
43166       ],
43167       [
43168         "Niue",
43169         "nu",
43170         "683"
43171       ],
43172       [
43173         "Norfolk Island",
43174         "nf",
43175         "672"
43176       ],
43177       [
43178         "North Korea (조선 민주주의 인민 공화국)",
43179         "kp",
43180         "850"
43181       ],
43182       [
43183         "Northern Mariana Islands",
43184         "mp",
43185         "1670"
43186       ],
43187       [
43188         "Norway (Norge)",
43189         "no",
43190         "47",
43191         0
43192       ],
43193       [
43194         "Oman (‫عُمان‬‎)",
43195         "om",
43196         "968"
43197       ],
43198       [
43199         "Pakistan (‫پاکستان‬‎)",
43200         "pk",
43201         "92"
43202       ],
43203       [
43204         "Palau",
43205         "pw",
43206         "680"
43207       ],
43208       [
43209         "Palestine (‫فلسطين‬‎)",
43210         "ps",
43211         "970"
43212       ],
43213       [
43214         "Panama (Panamá)",
43215         "pa",
43216         "507"
43217       ],
43218       [
43219         "Papua New Guinea",
43220         "pg",
43221         "675"
43222       ],
43223       [
43224         "Paraguay",
43225         "py",
43226         "595"
43227       ],
43228       [
43229         "Peru (Perú)",
43230         "pe",
43231         "51"
43232       ],
43233       [
43234         "Philippines",
43235         "ph",
43236         "63"
43237       ],
43238       [
43239         "Poland (Polska)",
43240         "pl",
43241         "48"
43242       ],
43243       [
43244         "Portugal",
43245         "pt",
43246         "351"
43247       ],
43248       [
43249         "Puerto Rico",
43250         "pr",
43251         "1",
43252         3,
43253         ["787", "939"]
43254       ],
43255       [
43256         "Qatar (‫قطر‬‎)",
43257         "qa",
43258         "974"
43259       ],
43260       [
43261         "Réunion (La Réunion)",
43262         "re",
43263         "262",
43264         0
43265       ],
43266       [
43267         "Romania (România)",
43268         "ro",
43269         "40"
43270       ],
43271       [
43272         "Russia (Россия)",
43273         "ru",
43274         "7",
43275         0
43276       ],
43277       [
43278         "Rwanda",
43279         "rw",
43280         "250"
43281       ],
43282       [
43283         "Saint Barthélemy",
43284         "bl",
43285         "590",
43286         1
43287       ],
43288       [
43289         "Saint Helena",
43290         "sh",
43291         "290"
43292       ],
43293       [
43294         "Saint Kitts and Nevis",
43295         "kn",
43296         "1869"
43297       ],
43298       [
43299         "Saint Lucia",
43300         "lc",
43301         "1758"
43302       ],
43303       [
43304         "Saint Martin (Saint-Martin (partie française))",
43305         "mf",
43306         "590",
43307         2
43308       ],
43309       [
43310         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43311         "pm",
43312         "508"
43313       ],
43314       [
43315         "Saint Vincent and the Grenadines",
43316         "vc",
43317         "1784"
43318       ],
43319       [
43320         "Samoa",
43321         "ws",
43322         "685"
43323       ],
43324       [
43325         "San Marino",
43326         "sm",
43327         "378"
43328       ],
43329       [
43330         "São Tomé and Príncipe (São Tomé e Príncipe)",
43331         "st",
43332         "239"
43333       ],
43334       [
43335         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43336         "sa",
43337         "966"
43338       ],
43339       [
43340         "Senegal (Sénégal)",
43341         "sn",
43342         "221"
43343       ],
43344       [
43345         "Serbia (Србија)",
43346         "rs",
43347         "381"
43348       ],
43349       [
43350         "Seychelles",
43351         "sc",
43352         "248"
43353       ],
43354       [
43355         "Sierra Leone",
43356         "sl",
43357         "232"
43358       ],
43359       [
43360         "Singapore",
43361         "sg",
43362         "65"
43363       ],
43364       [
43365         "Sint Maarten",
43366         "sx",
43367         "1721"
43368       ],
43369       [
43370         "Slovakia (Slovensko)",
43371         "sk",
43372         "421"
43373       ],
43374       [
43375         "Slovenia (Slovenija)",
43376         "si",
43377         "386"
43378       ],
43379       [
43380         "Solomon Islands",
43381         "sb",
43382         "677"
43383       ],
43384       [
43385         "Somalia (Soomaaliya)",
43386         "so",
43387         "252"
43388       ],
43389       [
43390         "South Africa",
43391         "za",
43392         "27"
43393       ],
43394       [
43395         "South Korea (대한민국)",
43396         "kr",
43397         "82"
43398       ],
43399       [
43400         "South Sudan (‫جنوب السودان‬‎)",
43401         "ss",
43402         "211"
43403       ],
43404       [
43405         "Spain (España)",
43406         "es",
43407         "34"
43408       ],
43409       [
43410         "Sri Lanka (ශ්‍රී ලංකාව)",
43411         "lk",
43412         "94"
43413       ],
43414       [
43415         "Sudan (‫السودان‬‎)",
43416         "sd",
43417         "249"
43418       ],
43419       [
43420         "Suriname",
43421         "sr",
43422         "597"
43423       ],
43424       [
43425         "Svalbard and Jan Mayen",
43426         "sj",
43427         "47",
43428         1
43429       ],
43430       [
43431         "Swaziland",
43432         "sz",
43433         "268"
43434       ],
43435       [
43436         "Sweden (Sverige)",
43437         "se",
43438         "46"
43439       ],
43440       [
43441         "Switzerland (Schweiz)",
43442         "ch",
43443         "41"
43444       ],
43445       [
43446         "Syria (‫سوريا‬‎)",
43447         "sy",
43448         "963"
43449       ],
43450       [
43451         "Taiwan (台灣)",
43452         "tw",
43453         "886"
43454       ],
43455       [
43456         "Tajikistan",
43457         "tj",
43458         "992"
43459       ],
43460       [
43461         "Tanzania",
43462         "tz",
43463         "255"
43464       ],
43465       [
43466         "Thailand (ไทย)",
43467         "th",
43468         "66"
43469       ],
43470       [
43471         "Timor-Leste",
43472         "tl",
43473         "670"
43474       ],
43475       [
43476         "Togo",
43477         "tg",
43478         "228"
43479       ],
43480       [
43481         "Tokelau",
43482         "tk",
43483         "690"
43484       ],
43485       [
43486         "Tonga",
43487         "to",
43488         "676"
43489       ],
43490       [
43491         "Trinidad and Tobago",
43492         "tt",
43493         "1868"
43494       ],
43495       [
43496         "Tunisia (‫تونس‬‎)",
43497         "tn",
43498         "216"
43499       ],
43500       [
43501         "Turkey (Türkiye)",
43502         "tr",
43503         "90"
43504       ],
43505       [
43506         "Turkmenistan",
43507         "tm",
43508         "993"
43509       ],
43510       [
43511         "Turks and Caicos Islands",
43512         "tc",
43513         "1649"
43514       ],
43515       [
43516         "Tuvalu",
43517         "tv",
43518         "688"
43519       ],
43520       [
43521         "U.S. Virgin Islands",
43522         "vi",
43523         "1340"
43524       ],
43525       [
43526         "Uganda",
43527         "ug",
43528         "256"
43529       ],
43530       [
43531         "Ukraine (Україна)",
43532         "ua",
43533         "380"
43534       ],
43535       [
43536         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43537         "ae",
43538         "971"
43539       ],
43540       [
43541         "United Kingdom",
43542         "gb",
43543         "44",
43544         0
43545       ],
43546       [
43547         "United States",
43548         "us",
43549         "1",
43550         0
43551       ],
43552       [
43553         "Uruguay",
43554         "uy",
43555         "598"
43556       ],
43557       [
43558         "Uzbekistan (Oʻzbekiston)",
43559         "uz",
43560         "998"
43561       ],
43562       [
43563         "Vanuatu",
43564         "vu",
43565         "678"
43566       ],
43567       [
43568         "Vatican City (Città del Vaticano)",
43569         "va",
43570         "39",
43571         1
43572       ],
43573       [
43574         "Venezuela",
43575         "ve",
43576         "58"
43577       ],
43578       [
43579         "Vietnam (Việt Nam)",
43580         "vn",
43581         "84"
43582       ],
43583       [
43584         "Wallis and Futuna (Wallis-et-Futuna)",
43585         "wf",
43586         "681"
43587       ],
43588       [
43589         "Western Sahara (‫الصحراء الغربية‬‎)",
43590         "eh",
43591         "212",
43592         1
43593       ],
43594       [
43595         "Yemen (‫اليمن‬‎)",
43596         "ye",
43597         "967"
43598       ],
43599       [
43600         "Zambia",
43601         "zm",
43602         "260"
43603       ],
43604       [
43605         "Zimbabwe",
43606         "zw",
43607         "263"
43608       ],
43609       [
43610         "Åland Islands",
43611         "ax",
43612         "358",
43613         1
43614       ]
43615   ];
43616   
43617   return d;
43618 }/**
43619 *    This script refer to:
43620 *    Title: International Telephone Input
43621 *    Author: Jack O'Connor
43622 *    Code version:  v12.1.12
43623 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43624 **/
43625
43626 /**
43627  * @class Roo.bootstrap.PhoneInput
43628  * @extends Roo.bootstrap.TriggerField
43629  * An input with International dial-code selection
43630  
43631  * @cfg {String} defaultDialCode default '+852'
43632  * @cfg {Array} preferedCountries default []
43633   
43634  * @constructor
43635  * Create a new PhoneInput.
43636  * @param {Object} config Configuration options
43637  */
43638
43639 Roo.bootstrap.PhoneInput = function(config) {
43640     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43641 };
43642
43643 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43644         /**
43645         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43646         */
43647         listWidth: undefined,
43648         
43649         selectedClass: 'active',
43650         
43651         invalidClass : "has-warning",
43652         
43653         validClass: 'has-success',
43654         
43655         allowed: '0123456789',
43656         
43657         max_length: 15,
43658         
43659         /**
43660          * @cfg {String} defaultDialCode The default dial code when initializing the input
43661          */
43662         defaultDialCode: '+852',
43663         
43664         /**
43665          * @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
43666          */
43667         preferedCountries: false,
43668         
43669         getAutoCreate : function()
43670         {
43671             var data = Roo.bootstrap.PhoneInputData();
43672             var align = this.labelAlign || this.parentLabelAlign();
43673             var id = Roo.id();
43674             
43675             this.allCountries = [];
43676             this.dialCodeMapping = [];
43677             
43678             for (var i = 0; i < data.length; i++) {
43679               var c = data[i];
43680               this.allCountries[i] = {
43681                 name: c[0],
43682                 iso2: c[1],
43683                 dialCode: c[2],
43684                 priority: c[3] || 0,
43685                 areaCodes: c[4] || null
43686               };
43687               this.dialCodeMapping[c[2]] = {
43688                   name: c[0],
43689                   iso2: c[1],
43690                   priority: c[3] || 0,
43691                   areaCodes: c[4] || null
43692               };
43693             }
43694             
43695             var cfg = {
43696                 cls: 'form-group',
43697                 cn: []
43698             };
43699             
43700             var input =  {
43701                 tag: 'input',
43702                 id : id,
43703                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43704                 maxlength: this.max_length,
43705                 cls : 'form-control tel-input',
43706                 autocomplete: 'new-password'
43707             };
43708             
43709             var hiddenInput = {
43710                 tag: 'input',
43711                 type: 'hidden',
43712                 cls: 'hidden-tel-input'
43713             };
43714             
43715             if (this.name) {
43716                 hiddenInput.name = this.name;
43717             }
43718             
43719             if (this.disabled) {
43720                 input.disabled = true;
43721             }
43722             
43723             var flag_container = {
43724                 tag: 'div',
43725                 cls: 'flag-box',
43726                 cn: [
43727                     {
43728                         tag: 'div',
43729                         cls: 'flag'
43730                     },
43731                     {
43732                         tag: 'div',
43733                         cls: 'caret'
43734                     }
43735                 ]
43736             };
43737             
43738             var box = {
43739                 tag: 'div',
43740                 cls: this.hasFeedback ? 'has-feedback' : '',
43741                 cn: [
43742                     hiddenInput,
43743                     input,
43744                     {
43745                         tag: 'input',
43746                         cls: 'dial-code-holder',
43747                         disabled: true
43748                     }
43749                 ]
43750             };
43751             
43752             var container = {
43753                 cls: 'roo-select2-container input-group',
43754                 cn: [
43755                     flag_container,
43756                     box
43757                 ]
43758             };
43759             
43760             if (this.fieldLabel.length) {
43761                 var indicator = {
43762                     tag: 'i',
43763                     tooltip: 'This field is required'
43764                 };
43765                 
43766                 var label = {
43767                     tag: 'label',
43768                     'for':  id,
43769                     cls: 'control-label',
43770                     cn: []
43771                 };
43772                 
43773                 var label_text = {
43774                     tag: 'span',
43775                     html: this.fieldLabel
43776                 };
43777                 
43778                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43779                 label.cn = [
43780                     indicator,
43781                     label_text
43782                 ];
43783                 
43784                 if(this.indicatorpos == 'right') {
43785                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43786                     label.cn = [
43787                         label_text,
43788                         indicator
43789                     ];
43790                 }
43791                 
43792                 if(align == 'left') {
43793                     container = {
43794                         tag: 'div',
43795                         cn: [
43796                             container
43797                         ]
43798                     };
43799                     
43800                     if(this.labelWidth > 12){
43801                         label.style = "width: " + this.labelWidth + 'px';
43802                     }
43803                     if(this.labelWidth < 13 && this.labelmd == 0){
43804                         this.labelmd = this.labelWidth;
43805                     }
43806                     if(this.labellg > 0){
43807                         label.cls += ' col-lg-' + this.labellg;
43808                         input.cls += ' col-lg-' + (12 - this.labellg);
43809                     }
43810                     if(this.labelmd > 0){
43811                         label.cls += ' col-md-' + this.labelmd;
43812                         container.cls += ' col-md-' + (12 - this.labelmd);
43813                     }
43814                     if(this.labelsm > 0){
43815                         label.cls += ' col-sm-' + this.labelsm;
43816                         container.cls += ' col-sm-' + (12 - this.labelsm);
43817                     }
43818                     if(this.labelxs > 0){
43819                         label.cls += ' col-xs-' + this.labelxs;
43820                         container.cls += ' col-xs-' + (12 - this.labelxs);
43821                     }
43822                 }
43823             }
43824             
43825             cfg.cn = [
43826                 label,
43827                 container
43828             ];
43829             
43830             var settings = this;
43831             
43832             ['xs','sm','md','lg'].map(function(size){
43833                 if (settings[size]) {
43834                     cfg.cls += ' col-' + size + '-' + settings[size];
43835                 }
43836             });
43837             
43838             this.store = new Roo.data.Store({
43839                 proxy : new Roo.data.MemoryProxy({}),
43840                 reader : new Roo.data.JsonReader({
43841                     fields : [
43842                         {
43843                             'name' : 'name',
43844                             'type' : 'string'
43845                         },
43846                         {
43847                             'name' : 'iso2',
43848                             'type' : 'string'
43849                         },
43850                         {
43851                             'name' : 'dialCode',
43852                             'type' : 'string'
43853                         },
43854                         {
43855                             'name' : 'priority',
43856                             'type' : 'string'
43857                         },
43858                         {
43859                             'name' : 'areaCodes',
43860                             'type' : 'string'
43861                         }
43862                     ]
43863                 })
43864             });
43865             
43866             if(!this.preferedCountries) {
43867                 this.preferedCountries = [
43868                     'hk',
43869                     'gb',
43870                     'us'
43871                 ];
43872             }
43873             
43874             var p = this.preferedCountries.reverse();
43875             
43876             if(p) {
43877                 for (var i = 0; i < p.length; i++) {
43878                     for (var j = 0; j < this.allCountries.length; j++) {
43879                         if(this.allCountries[j].iso2 == p[i]) {
43880                             var t = this.allCountries[j];
43881                             this.allCountries.splice(j,1);
43882                             this.allCountries.unshift(t);
43883                         }
43884                     } 
43885                 }
43886             }
43887             
43888             this.store.proxy.data = {
43889                 success: true,
43890                 data: this.allCountries
43891             };
43892             
43893             return cfg;
43894         },
43895         
43896         initEvents : function()
43897         {
43898             this.createList();
43899             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43900             
43901             this.indicator = this.indicatorEl();
43902             this.flag = this.flagEl();
43903             this.dialCodeHolder = this.dialCodeHolderEl();
43904             
43905             this.trigger = this.el.select('div.flag-box',true).first();
43906             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43907             
43908             var _this = this;
43909             
43910             (function(){
43911                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43912                 _this.list.setWidth(lw);
43913             }).defer(100);
43914             
43915             this.list.on('mouseover', this.onViewOver, this);
43916             this.list.on('mousemove', this.onViewMove, this);
43917             this.inputEl().on("keyup", this.onKeyUp, this);
43918             this.inputEl().on("keypress", this.onKeyPress, this);
43919             
43920             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43921
43922             this.view = new Roo.View(this.list, this.tpl, {
43923                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43924             });
43925             
43926             this.view.on('click', this.onViewClick, this);
43927             this.setValue(this.defaultDialCode);
43928         },
43929         
43930         onTriggerClick : function(e)
43931         {
43932             Roo.log('trigger click');
43933             if(this.disabled){
43934                 return;
43935             }
43936             
43937             if(this.isExpanded()){
43938                 this.collapse();
43939                 this.hasFocus = false;
43940             }else {
43941                 this.store.load({});
43942                 this.hasFocus = true;
43943                 this.expand();
43944             }
43945         },
43946         
43947         isExpanded : function()
43948         {
43949             return this.list.isVisible();
43950         },
43951         
43952         collapse : function()
43953         {
43954             if(!this.isExpanded()){
43955                 return;
43956             }
43957             this.list.hide();
43958             Roo.get(document).un('mousedown', this.collapseIf, this);
43959             Roo.get(document).un('mousewheel', this.collapseIf, this);
43960             this.fireEvent('collapse', this);
43961             this.validate();
43962         },
43963         
43964         expand : function()
43965         {
43966             Roo.log('expand');
43967
43968             if(this.isExpanded() || !this.hasFocus){
43969                 return;
43970             }
43971             
43972             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43973             this.list.setWidth(lw);
43974             
43975             this.list.show();
43976             this.restrictHeight();
43977             
43978             Roo.get(document).on('mousedown', this.collapseIf, this);
43979             Roo.get(document).on('mousewheel', this.collapseIf, this);
43980             
43981             this.fireEvent('expand', this);
43982         },
43983         
43984         restrictHeight : function()
43985         {
43986             this.list.alignTo(this.inputEl(), this.listAlign);
43987             this.list.alignTo(this.inputEl(), this.listAlign);
43988         },
43989         
43990         onViewOver : function(e, t)
43991         {
43992             if(this.inKeyMode){
43993                 return;
43994             }
43995             var item = this.view.findItemFromChild(t);
43996             
43997             if(item){
43998                 var index = this.view.indexOf(item);
43999                 this.select(index, false);
44000             }
44001         },
44002
44003         // private
44004         onViewClick : function(view, doFocus, el, e)
44005         {
44006             var index = this.view.getSelectedIndexes()[0];
44007             
44008             var r = this.store.getAt(index);
44009             
44010             if(r){
44011                 this.onSelect(r, index);
44012             }
44013             if(doFocus !== false && !this.blockFocus){
44014                 this.inputEl().focus();
44015             }
44016         },
44017         
44018         onViewMove : function(e, t)
44019         {
44020             this.inKeyMode = false;
44021         },
44022         
44023         select : function(index, scrollIntoView)
44024         {
44025             this.selectedIndex = index;
44026             this.view.select(index);
44027             if(scrollIntoView !== false){
44028                 var el = this.view.getNode(index);
44029                 if(el){
44030                     this.list.scrollChildIntoView(el, false);
44031                 }
44032             }
44033         },
44034         
44035         createList : function()
44036         {
44037             this.list = Roo.get(document.body).createChild({
44038                 tag: 'ul',
44039                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44040                 style: 'display:none'
44041             });
44042             
44043             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44044         },
44045         
44046         collapseIf : function(e)
44047         {
44048             var in_combo  = e.within(this.el);
44049             var in_list =  e.within(this.list);
44050             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44051             
44052             if (in_combo || in_list || is_list) {
44053                 return;
44054             }
44055             this.collapse();
44056         },
44057         
44058         onSelect : function(record, index)
44059         {
44060             if(this.fireEvent('beforeselect', this, record, index) !== false){
44061                 
44062                 this.setFlagClass(record.data.iso2);
44063                 this.setDialCode(record.data.dialCode);
44064                 this.hasFocus = false;
44065                 this.collapse();
44066                 this.fireEvent('select', this, record, index);
44067             }
44068         },
44069         
44070         flagEl : function()
44071         {
44072             var flag = this.el.select('div.flag',true).first();
44073             if(!flag){
44074                 return false;
44075             }
44076             return flag;
44077         },
44078         
44079         dialCodeHolderEl : function()
44080         {
44081             var d = this.el.select('input.dial-code-holder',true).first();
44082             if(!d){
44083                 return false;
44084             }
44085             return d;
44086         },
44087         
44088         setDialCode : function(v)
44089         {
44090             this.dialCodeHolder.dom.value = '+'+v;
44091         },
44092         
44093         setFlagClass : function(n)
44094         {
44095             this.flag.dom.className = 'flag '+n;
44096         },
44097         
44098         getValue : function()
44099         {
44100             var v = this.inputEl().getValue();
44101             if(this.dialCodeHolder) {
44102                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44103             }
44104             return v;
44105         },
44106         
44107         setValue : function(v)
44108         {
44109             var d = this.getDialCode(v);
44110             
44111             //invalid dial code
44112             if(v.length == 0 || !d || d.length == 0) {
44113                 if(this.rendered){
44114                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44115                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44116                 }
44117                 return;
44118             }
44119             
44120             //valid dial code
44121             this.setFlagClass(this.dialCodeMapping[d].iso2);
44122             this.setDialCode(d);
44123             this.inputEl().dom.value = v.replace('+'+d,'');
44124             this.hiddenEl().dom.value = this.getValue();
44125             
44126             this.validate();
44127         },
44128         
44129         getDialCode : function(v)
44130         {
44131             v = v ||  '';
44132             
44133             if (v.length == 0) {
44134                 return this.dialCodeHolder.dom.value;
44135             }
44136             
44137             var dialCode = "";
44138             if (v.charAt(0) != "+") {
44139                 return false;
44140             }
44141             var numericChars = "";
44142             for (var i = 1; i < v.length; i++) {
44143               var c = v.charAt(i);
44144               if (!isNaN(c)) {
44145                 numericChars += c;
44146                 if (this.dialCodeMapping[numericChars]) {
44147                   dialCode = v.substr(1, i);
44148                 }
44149                 if (numericChars.length == 4) {
44150                   break;
44151                 }
44152               }
44153             }
44154             return dialCode;
44155         },
44156         
44157         reset : function()
44158         {
44159             this.setValue(this.defaultDialCode);
44160             this.validate();
44161         },
44162         
44163         hiddenEl : function()
44164         {
44165             return this.el.select('input.hidden-tel-input',true).first();
44166         },
44167         
44168         // after setting val
44169         onKeyUp : function(e){
44170             this.setValue(this.getValue());
44171         },
44172         
44173         onKeyPress : function(e){
44174             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44175                 e.stopEvent();
44176             }
44177         }
44178         
44179 });
44180 /**
44181  * @class Roo.bootstrap.MoneyField
44182  * @extends Roo.bootstrap.ComboBox
44183  * Bootstrap MoneyField class
44184  * 
44185  * @constructor
44186  * Create a new MoneyField.
44187  * @param {Object} config Configuration options
44188  */
44189
44190 Roo.bootstrap.MoneyField = function(config) {
44191     
44192     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44193     
44194 };
44195
44196 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44197     
44198     /**
44199      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44200      */
44201     allowDecimals : true,
44202     /**
44203      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44204      */
44205     decimalSeparator : ".",
44206     /**
44207      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44208      */
44209     decimalPrecision : 0,
44210     /**
44211      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44212      */
44213     allowNegative : true,
44214     /**
44215      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44216      */
44217     allowZero: true,
44218     /**
44219      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44220      */
44221     minValue : Number.NEGATIVE_INFINITY,
44222     /**
44223      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44224      */
44225     maxValue : Number.MAX_VALUE,
44226     /**
44227      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44228      */
44229     minText : "The minimum value for this field is {0}",
44230     /**
44231      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44232      */
44233     maxText : "The maximum value for this field is {0}",
44234     /**
44235      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44236      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44237      */
44238     nanText : "{0} is not a valid number",
44239     /**
44240      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44241      */
44242     castInt : true,
44243     /**
44244      * @cfg {String} defaults currency of the MoneyField
44245      * value should be in lkey
44246      */
44247     defaultCurrency : false,
44248     /**
44249      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44250      */
44251     thousandsDelimiter : false,
44252     /**
44253      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44254      */
44255     max_length: false,
44256     
44257     inputlg : 9,
44258     inputmd : 9,
44259     inputsm : 9,
44260     inputxs : 6,
44261     
44262     store : false,
44263     
44264     getAutoCreate : function()
44265     {
44266         var align = this.labelAlign || this.parentLabelAlign();
44267         
44268         var id = Roo.id();
44269
44270         var cfg = {
44271             cls: 'form-group',
44272             cn: []
44273         };
44274
44275         var input =  {
44276             tag: 'input',
44277             id : id,
44278             cls : 'form-control roo-money-amount-input',
44279             autocomplete: 'new-password'
44280         };
44281         
44282         var hiddenInput = {
44283             tag: 'input',
44284             type: 'hidden',
44285             id: Roo.id(),
44286             cls: 'hidden-number-input'
44287         };
44288         
44289         if(this.max_length) {
44290             input.maxlength = this.max_length; 
44291         }
44292         
44293         if (this.name) {
44294             hiddenInput.name = this.name;
44295         }
44296
44297         if (this.disabled) {
44298             input.disabled = true;
44299         }
44300
44301         var clg = 12 - this.inputlg;
44302         var cmd = 12 - this.inputmd;
44303         var csm = 12 - this.inputsm;
44304         var cxs = 12 - this.inputxs;
44305         
44306         var container = {
44307             tag : 'div',
44308             cls : 'row roo-money-field',
44309             cn : [
44310                 {
44311                     tag : 'div',
44312                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44313                     cn : [
44314                         {
44315                             tag : 'div',
44316                             cls: 'roo-select2-container input-group',
44317                             cn: [
44318                                 {
44319                                     tag : 'input',
44320                                     cls : 'form-control roo-money-currency-input',
44321                                     autocomplete: 'new-password',
44322                                     readOnly : 1,
44323                                     name : this.currencyName
44324                                 },
44325                                 {
44326                                     tag :'span',
44327                                     cls : 'input-group-addon',
44328                                     cn : [
44329                                         {
44330                                             tag: 'span',
44331                                             cls: 'caret'
44332                                         }
44333                                     ]
44334                                 }
44335                             ]
44336                         }
44337                     ]
44338                 },
44339                 {
44340                     tag : 'div',
44341                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44342                     cn : [
44343                         {
44344                             tag: 'div',
44345                             cls: this.hasFeedback ? 'has-feedback' : '',
44346                             cn: [
44347                                 input
44348                             ]
44349                         }
44350                     ]
44351                 }
44352             ]
44353             
44354         };
44355         
44356         if (this.fieldLabel.length) {
44357             var indicator = {
44358                 tag: 'i',
44359                 tooltip: 'This field is required'
44360             };
44361
44362             var label = {
44363                 tag: 'label',
44364                 'for':  id,
44365                 cls: 'control-label',
44366                 cn: []
44367             };
44368
44369             var label_text = {
44370                 tag: 'span',
44371                 html: this.fieldLabel
44372             };
44373
44374             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44375             label.cn = [
44376                 indicator,
44377                 label_text
44378             ];
44379
44380             if(this.indicatorpos == 'right') {
44381                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44382                 label.cn = [
44383                     label_text,
44384                     indicator
44385                 ];
44386             }
44387
44388             if(align == 'left') {
44389                 container = {
44390                     tag: 'div',
44391                     cn: [
44392                         container
44393                     ]
44394                 };
44395
44396                 if(this.labelWidth > 12){
44397                     label.style = "width: " + this.labelWidth + 'px';
44398                 }
44399                 if(this.labelWidth < 13 && this.labelmd == 0){
44400                     this.labelmd = this.labelWidth;
44401                 }
44402                 if(this.labellg > 0){
44403                     label.cls += ' col-lg-' + this.labellg;
44404                     input.cls += ' col-lg-' + (12 - this.labellg);
44405                 }
44406                 if(this.labelmd > 0){
44407                     label.cls += ' col-md-' + this.labelmd;
44408                     container.cls += ' col-md-' + (12 - this.labelmd);
44409                 }
44410                 if(this.labelsm > 0){
44411                     label.cls += ' col-sm-' + this.labelsm;
44412                     container.cls += ' col-sm-' + (12 - this.labelsm);
44413                 }
44414                 if(this.labelxs > 0){
44415                     label.cls += ' col-xs-' + this.labelxs;
44416                     container.cls += ' col-xs-' + (12 - this.labelxs);
44417                 }
44418             }
44419         }
44420
44421         cfg.cn = [
44422             label,
44423             container,
44424             hiddenInput
44425         ];
44426         
44427         var settings = this;
44428
44429         ['xs','sm','md','lg'].map(function(size){
44430             if (settings[size]) {
44431                 cfg.cls += ' col-' + size + '-' + settings[size];
44432             }
44433         });
44434         
44435         return cfg;
44436     },
44437     
44438     initEvents : function()
44439     {
44440         this.indicator = this.indicatorEl();
44441         
44442         this.initCurrencyEvent();
44443         
44444         this.initNumberEvent();
44445     },
44446     
44447     initCurrencyEvent : function()
44448     {
44449         if (!this.store) {
44450             throw "can not find store for combo";
44451         }
44452         
44453         this.store = Roo.factory(this.store, Roo.data);
44454         this.store.parent = this;
44455         
44456         this.createList();
44457         
44458         this.triggerEl = this.el.select('.input-group-addon', true).first();
44459         
44460         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44461         
44462         var _this = this;
44463         
44464         (function(){
44465             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44466             _this.list.setWidth(lw);
44467         }).defer(100);
44468         
44469         this.list.on('mouseover', this.onViewOver, this);
44470         this.list.on('mousemove', this.onViewMove, this);
44471         this.list.on('scroll', this.onViewScroll, this);
44472         
44473         if(!this.tpl){
44474             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44475         }
44476         
44477         this.view = new Roo.View(this.list, this.tpl, {
44478             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44479         });
44480         
44481         this.view.on('click', this.onViewClick, this);
44482         
44483         this.store.on('beforeload', this.onBeforeLoad, this);
44484         this.store.on('load', this.onLoad, this);
44485         this.store.on('loadexception', this.onLoadException, this);
44486         
44487         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44488             "up" : function(e){
44489                 this.inKeyMode = true;
44490                 this.selectPrev();
44491             },
44492
44493             "down" : function(e){
44494                 if(!this.isExpanded()){
44495                     this.onTriggerClick();
44496                 }else{
44497                     this.inKeyMode = true;
44498                     this.selectNext();
44499                 }
44500             },
44501
44502             "enter" : function(e){
44503                 this.collapse();
44504                 
44505                 if(this.fireEvent("specialkey", this, e)){
44506                     this.onViewClick(false);
44507                 }
44508                 
44509                 return true;
44510             },
44511
44512             "esc" : function(e){
44513                 this.collapse();
44514             },
44515
44516             "tab" : function(e){
44517                 this.collapse();
44518                 
44519                 if(this.fireEvent("specialkey", this, e)){
44520                     this.onViewClick(false);
44521                 }
44522                 
44523                 return true;
44524             },
44525
44526             scope : this,
44527
44528             doRelay : function(foo, bar, hname){
44529                 if(hname == 'down' || this.scope.isExpanded()){
44530                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44531                 }
44532                 return true;
44533             },
44534
44535             forceKeyDown: true
44536         });
44537         
44538         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44539         
44540     },
44541     
44542     initNumberEvent : function(e)
44543     {
44544         this.inputEl().on("keydown" , this.fireKey,  this);
44545         this.inputEl().on("focus", this.onFocus,  this);
44546         this.inputEl().on("blur", this.onBlur,  this);
44547         
44548         this.inputEl().relayEvent('keyup', this);
44549         
44550         if(this.indicator){
44551             this.indicator.addClass('invisible');
44552         }
44553  
44554         this.originalValue = this.getValue();
44555         
44556         if(this.validationEvent == 'keyup'){
44557             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44558             this.inputEl().on('keyup', this.filterValidation, this);
44559         }
44560         else if(this.validationEvent !== false){
44561             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44562         }
44563         
44564         if(this.selectOnFocus){
44565             this.on("focus", this.preFocus, this);
44566             
44567         }
44568         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44569             this.inputEl().on("keypress", this.filterKeys, this);
44570         } else {
44571             this.inputEl().relayEvent('keypress', this);
44572         }
44573         
44574         var allowed = "0123456789";
44575         
44576         if(this.allowDecimals){
44577             allowed += this.decimalSeparator;
44578         }
44579         
44580         if(this.allowNegative){
44581             allowed += "-";
44582         }
44583         
44584         if(this.thousandsDelimiter) {
44585             allowed += ",";
44586         }
44587         
44588         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44589         
44590         var keyPress = function(e){
44591             
44592             var k = e.getKey();
44593             
44594             var c = e.getCharCode();
44595             
44596             if(
44597                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44598                     allowed.indexOf(String.fromCharCode(c)) === -1
44599             ){
44600                 e.stopEvent();
44601                 return;
44602             }
44603             
44604             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44605                 return;
44606             }
44607             
44608             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44609                 e.stopEvent();
44610             }
44611         };
44612         
44613         this.inputEl().on("keypress", keyPress, this);
44614         
44615     },
44616     
44617     onTriggerClick : function(e)
44618     {   
44619         if(this.disabled){
44620             return;
44621         }
44622         
44623         this.page = 0;
44624         this.loadNext = false;
44625         
44626         if(this.isExpanded()){
44627             this.collapse();
44628             return;
44629         }
44630         
44631         this.hasFocus = true;
44632         
44633         if(this.triggerAction == 'all') {
44634             this.doQuery(this.allQuery, true);
44635             return;
44636         }
44637         
44638         this.doQuery(this.getRawValue());
44639     },
44640     
44641     getCurrency : function()
44642     {   
44643         var v = this.currencyEl().getValue();
44644         
44645         return v;
44646     },
44647     
44648     restrictHeight : function()
44649     {
44650         this.list.alignTo(this.currencyEl(), this.listAlign);
44651         this.list.alignTo(this.currencyEl(), this.listAlign);
44652     },
44653     
44654     onViewClick : function(view, doFocus, el, e)
44655     {
44656         var index = this.view.getSelectedIndexes()[0];
44657         
44658         var r = this.store.getAt(index);
44659         
44660         if(r){
44661             this.onSelect(r, index);
44662         }
44663     },
44664     
44665     onSelect : function(record, index){
44666         
44667         if(this.fireEvent('beforeselect', this, record, index) !== false){
44668         
44669             this.setFromCurrencyData(index > -1 ? record.data : false);
44670             
44671             this.collapse();
44672             
44673             this.fireEvent('select', this, record, index);
44674         }
44675     },
44676     
44677     setFromCurrencyData : function(o)
44678     {
44679         var currency = '';
44680         
44681         this.lastCurrency = o;
44682         
44683         if (this.currencyField) {
44684             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44685         } else {
44686             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44687         }
44688         
44689         this.lastSelectionText = currency;
44690         
44691         //setting default currency
44692         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44693             this.setCurrency(this.defaultCurrency);
44694             return;
44695         }
44696         
44697         this.setCurrency(currency);
44698     },
44699     
44700     setFromData : function(o)
44701     {
44702         var c = {};
44703         
44704         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44705         
44706         this.setFromCurrencyData(c);
44707         
44708         var value = '';
44709         
44710         if (this.name) {
44711             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44712         } else {
44713             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44714         }
44715         
44716         this.setValue(value);
44717         
44718     },
44719     
44720     setCurrency : function(v)
44721     {   
44722         this.currencyValue = v;
44723         
44724         if(this.rendered){
44725             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44726             this.validate();
44727         }
44728     },
44729     
44730     setValue : function(v)
44731     {
44732         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44733         
44734         this.value = v;
44735         
44736         if(this.rendered){
44737             
44738             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44739             
44740             this.inputEl().dom.value = (v == '') ? '' :
44741                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44742             
44743             if(!this.allowZero && v === '0') {
44744                 this.hiddenEl().dom.value = '';
44745                 this.inputEl().dom.value = '';
44746             }
44747             
44748             this.validate();
44749         }
44750     },
44751     
44752     getRawValue : function()
44753     {
44754         var v = this.inputEl().getValue();
44755         
44756         return v;
44757     },
44758     
44759     getValue : function()
44760     {
44761         return this.fixPrecision(this.parseValue(this.getRawValue()));
44762     },
44763     
44764     parseValue : function(value)
44765     {
44766         if(this.thousandsDelimiter) {
44767             value += "";
44768             r = new RegExp(",", "g");
44769             value = value.replace(r, "");
44770         }
44771         
44772         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44773         return isNaN(value) ? '' : value;
44774         
44775     },
44776     
44777     fixPrecision : function(value)
44778     {
44779         if(this.thousandsDelimiter) {
44780             value += "";
44781             r = new RegExp(",", "g");
44782             value = value.replace(r, "");
44783         }
44784         
44785         var nan = isNaN(value);
44786         
44787         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44788             return nan ? '' : value;
44789         }
44790         return parseFloat(value).toFixed(this.decimalPrecision);
44791     },
44792     
44793     decimalPrecisionFcn : function(v)
44794     {
44795         return Math.floor(v);
44796     },
44797     
44798     validateValue : function(value)
44799     {
44800         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44801             return false;
44802         }
44803         
44804         var num = this.parseValue(value);
44805         
44806         if(isNaN(num)){
44807             this.markInvalid(String.format(this.nanText, value));
44808             return false;
44809         }
44810         
44811         if(num < this.minValue){
44812             this.markInvalid(String.format(this.minText, this.minValue));
44813             return false;
44814         }
44815         
44816         if(num > this.maxValue){
44817             this.markInvalid(String.format(this.maxText, this.maxValue));
44818             return false;
44819         }
44820         
44821         return true;
44822     },
44823     
44824     validate : function()
44825     {
44826         if(this.disabled || this.allowBlank){
44827             this.markValid();
44828             return true;
44829         }
44830         
44831         var currency = this.getCurrency();
44832         
44833         if(this.validateValue(this.getRawValue()) && currency.length){
44834             this.markValid();
44835             return true;
44836         }
44837         
44838         this.markInvalid();
44839         return false;
44840     },
44841     
44842     getName: function()
44843     {
44844         return this.name;
44845     },
44846     
44847     beforeBlur : function()
44848     {
44849         if(!this.castInt){
44850             return;
44851         }
44852         
44853         var v = this.parseValue(this.getRawValue());
44854         
44855         if(v || v == 0){
44856             this.setValue(v);
44857         }
44858     },
44859     
44860     onBlur : function()
44861     {
44862         this.beforeBlur();
44863         
44864         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44865             //this.el.removeClass(this.focusClass);
44866         }
44867         
44868         this.hasFocus = false;
44869         
44870         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44871             this.validate();
44872         }
44873         
44874         var v = this.getValue();
44875         
44876         if(String(v) !== String(this.startValue)){
44877             this.fireEvent('change', this, v, this.startValue);
44878         }
44879         
44880         this.fireEvent("blur", this);
44881     },
44882     
44883     inputEl : function()
44884     {
44885         return this.el.select('.roo-money-amount-input', true).first();
44886     },
44887     
44888     currencyEl : function()
44889     {
44890         return this.el.select('.roo-money-currency-input', true).first();
44891     },
44892     
44893     hiddenEl : function()
44894     {
44895         return this.el.select('input.hidden-number-input',true).first();
44896     }
44897     
44898 });/**
44899  * @class Roo.bootstrap.BezierSignature
44900  * @extends Roo.bootstrap.Component
44901  * Bootstrap BezierSignature class
44902  * This script refer to:
44903  *    Title: Signature Pad
44904  *    Author: szimek
44905  *    Availability: https://github.com/szimek/signature_pad
44906  *
44907  * @constructor
44908  * Create a new BezierSignature
44909  * @param {Object} config The config object
44910  */
44911
44912 Roo.bootstrap.BezierSignature = function(config){
44913     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44914     this.addEvents({
44915         "resize" : true
44916     });
44917 };
44918
44919 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44920 {
44921      
44922     curve_data: [],
44923     
44924     is_empty: true,
44925     
44926     mouse_btn_down: true,
44927     
44928     /**
44929      * @cfg {int} canvas height
44930      */
44931     canvas_height: '200px',
44932     
44933     /**
44934      * @cfg {float|function} Radius of a single dot.
44935      */ 
44936     dot_size: false,
44937     
44938     /**
44939      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44940      */
44941     min_width: 0.5,
44942     
44943     /**
44944      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44945      */
44946     max_width: 2.5,
44947     
44948     /**
44949      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44950      */
44951     throttle: 16,
44952     
44953     /**
44954      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44955      */
44956     min_distance: 5,
44957     
44958     /**
44959      * @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.
44960      */
44961     bg_color: 'rgba(0, 0, 0, 0)',
44962     
44963     /**
44964      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44965      */
44966     dot_color: 'black',
44967     
44968     /**
44969      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44970      */ 
44971     velocity_filter_weight: 0.7,
44972     
44973     /**
44974      * @cfg {function} Callback when stroke begin. 
44975      */
44976     onBegin: false,
44977     
44978     /**
44979      * @cfg {function} Callback when stroke end.
44980      */
44981     onEnd: false,
44982     
44983     getAutoCreate : function()
44984     {
44985         var cls = 'roo-signature column';
44986         
44987         if(this.cls){
44988             cls += ' ' + this.cls;
44989         }
44990         
44991         var col_sizes = [
44992             'lg',
44993             'md',
44994             'sm',
44995             'xs'
44996         ];
44997         
44998         for(var i = 0; i < col_sizes.length; i++) {
44999             if(this[col_sizes[i]]) {
45000                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
45001             }
45002         }
45003         
45004         var cfg = {
45005             tag: 'div',
45006             cls: cls,
45007             cn: [
45008                 {
45009                     tag: 'div',
45010                     cls: 'roo-signature-body',
45011                     cn: [
45012                         {
45013                             tag: 'canvas',
45014                             cls: 'roo-signature-body-canvas',
45015                             height: this.canvas_height,
45016                             width: this.canvas_width
45017                         }
45018                     ]
45019                 },
45020                 {
45021                     tag: 'input',
45022                     type: 'file',
45023                     style: 'display: none'
45024                 }
45025             ]
45026         };
45027         
45028         return cfg;
45029     },
45030     
45031     initEvents: function() 
45032     {
45033         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45034         
45035         var canvas = this.canvasEl();
45036         
45037         // mouse && touch event swapping...
45038         canvas.dom.style.touchAction = 'none';
45039         canvas.dom.style.msTouchAction = 'none';
45040         
45041         this.mouse_btn_down = false;
45042         canvas.on('mousedown', this._handleMouseDown, this);
45043         canvas.on('mousemove', this._handleMouseMove, this);
45044         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45045         
45046         if (window.PointerEvent) {
45047             canvas.on('pointerdown', this._handleMouseDown, this);
45048             canvas.on('pointermove', this._handleMouseMove, this);
45049             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45050         }
45051         
45052         if ('ontouchstart' in window) {
45053             canvas.on('touchstart', this._handleTouchStart, this);
45054             canvas.on('touchmove', this._handleTouchMove, this);
45055             canvas.on('touchend', this._handleTouchEnd, this);
45056         }
45057         
45058         Roo.EventManager.onWindowResize(this.resize, this, true);
45059         
45060         // file input event
45061         this.fileEl().on('change', this.uploadImage, this);
45062         
45063         this.clear();
45064         
45065         this.resize();
45066     },
45067     
45068     resize: function(){
45069         
45070         var canvas = this.canvasEl().dom;
45071         var ctx = this.canvasElCtx();
45072         var img_data = false;
45073         
45074         if(canvas.width > 0) {
45075             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45076         }
45077         // setting canvas width will clean img data
45078         canvas.width = 0;
45079         
45080         var style = window.getComputedStyle ? 
45081             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45082             
45083         var padding_left = parseInt(style.paddingLeft) || 0;
45084         var padding_right = parseInt(style.paddingRight) || 0;
45085         
45086         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45087         
45088         if(img_data) {
45089             ctx.putImageData(img_data, 0, 0);
45090         }
45091     },
45092     
45093     _handleMouseDown: function(e)
45094     {
45095         if (e.browserEvent.which === 1) {
45096             this.mouse_btn_down = true;
45097             this.strokeBegin(e);
45098         }
45099     },
45100     
45101     _handleMouseMove: function (e)
45102     {
45103         if (this.mouse_btn_down) {
45104             this.strokeMoveUpdate(e);
45105         }
45106     },
45107     
45108     _handleMouseUp: function (e)
45109     {
45110         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45111             this.mouse_btn_down = false;
45112             this.strokeEnd(e);
45113         }
45114     },
45115     
45116     _handleTouchStart: function (e) {
45117         
45118         e.preventDefault();
45119         if (e.browserEvent.targetTouches.length === 1) {
45120             // var touch = e.browserEvent.changedTouches[0];
45121             // this.strokeBegin(touch);
45122             
45123              this.strokeBegin(e); // assume e catching the correct xy...
45124         }
45125     },
45126     
45127     _handleTouchMove: function (e) {
45128         e.preventDefault();
45129         // var touch = event.targetTouches[0];
45130         // _this._strokeMoveUpdate(touch);
45131         this.strokeMoveUpdate(e);
45132     },
45133     
45134     _handleTouchEnd: function (e) {
45135         var wasCanvasTouched = e.target === this.canvasEl().dom;
45136         if (wasCanvasTouched) {
45137             e.preventDefault();
45138             // var touch = event.changedTouches[0];
45139             // _this._strokeEnd(touch);
45140             this.strokeEnd(e);
45141         }
45142     },
45143     
45144     reset: function () {
45145         this._lastPoints = [];
45146         this._lastVelocity = 0;
45147         this._lastWidth = (this.min_width + this.max_width) / 2;
45148         this.canvasElCtx().fillStyle = this.dot_color;
45149     },
45150     
45151     strokeMoveUpdate: function(e)
45152     {
45153         this.strokeUpdate(e);
45154         
45155         if (this.throttle) {
45156             this.throttleStroke(this.strokeUpdate, this.throttle);
45157         }
45158         else {
45159             this.strokeUpdate(e);
45160         }
45161     },
45162     
45163     strokeBegin: function(e)
45164     {
45165         var newPointGroup = {
45166             color: this.dot_color,
45167             points: []
45168         };
45169         
45170         if (typeof this.onBegin === 'function') {
45171             this.onBegin(e);
45172         }
45173         
45174         this.curve_data.push(newPointGroup);
45175         this.reset();
45176         this.strokeUpdate(e);
45177     },
45178     
45179     strokeUpdate: function(e)
45180     {
45181         var rect = this.canvasEl().dom.getBoundingClientRect();
45182         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45183         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45184         var lastPoints = lastPointGroup.points;
45185         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45186         var isLastPointTooClose = lastPoint
45187             ? point.distanceTo(lastPoint) <= this.min_distance
45188             : false;
45189         var color = lastPointGroup.color;
45190         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45191             var curve = this.addPoint(point);
45192             if (!lastPoint) {
45193                 this.drawDot({color: color, point: point});
45194             }
45195             else if (curve) {
45196                 this.drawCurve({color: color, curve: curve});
45197             }
45198             lastPoints.push({
45199                 time: point.time,
45200                 x: point.x,
45201                 y: point.y
45202             });
45203         }
45204     },
45205     
45206     strokeEnd: function(e)
45207     {
45208         this.strokeUpdate(e);
45209         if (typeof this.onEnd === 'function') {
45210             this.onEnd(e);
45211         }
45212     },
45213     
45214     addPoint:  function (point) {
45215         var _lastPoints = this._lastPoints;
45216         _lastPoints.push(point);
45217         if (_lastPoints.length > 2) {
45218             if (_lastPoints.length === 3) {
45219                 _lastPoints.unshift(_lastPoints[0]);
45220             }
45221             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45222             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45223             _lastPoints.shift();
45224             return curve;
45225         }
45226         return null;
45227     },
45228     
45229     calculateCurveWidths: function (startPoint, endPoint) {
45230         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45231             (1 - this.velocity_filter_weight) * this._lastVelocity;
45232
45233         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45234         var widths = {
45235             end: newWidth,
45236             start: this._lastWidth
45237         };
45238         
45239         this._lastVelocity = velocity;
45240         this._lastWidth = newWidth;
45241         return widths;
45242     },
45243     
45244     drawDot: function (_a) {
45245         var color = _a.color, point = _a.point;
45246         var ctx = this.canvasElCtx();
45247         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45248         ctx.beginPath();
45249         this.drawCurveSegment(point.x, point.y, width);
45250         ctx.closePath();
45251         ctx.fillStyle = color;
45252         ctx.fill();
45253     },
45254     
45255     drawCurve: function (_a) {
45256         var color = _a.color, curve = _a.curve;
45257         var ctx = this.canvasElCtx();
45258         var widthDelta = curve.endWidth - curve.startWidth;
45259         var drawSteps = Math.floor(curve.length()) * 2;
45260         ctx.beginPath();
45261         ctx.fillStyle = color;
45262         for (var i = 0; i < drawSteps; i += 1) {
45263         var t = i / drawSteps;
45264         var tt = t * t;
45265         var ttt = tt * t;
45266         var u = 1 - t;
45267         var uu = u * u;
45268         var uuu = uu * u;
45269         var x = uuu * curve.startPoint.x;
45270         x += 3 * uu * t * curve.control1.x;
45271         x += 3 * u * tt * curve.control2.x;
45272         x += ttt * curve.endPoint.x;
45273         var y = uuu * curve.startPoint.y;
45274         y += 3 * uu * t * curve.control1.y;
45275         y += 3 * u * tt * curve.control2.y;
45276         y += ttt * curve.endPoint.y;
45277         var width = curve.startWidth + ttt * widthDelta;
45278         this.drawCurveSegment(x, y, width);
45279         }
45280         ctx.closePath();
45281         ctx.fill();
45282     },
45283     
45284     drawCurveSegment: function (x, y, width) {
45285         var ctx = this.canvasElCtx();
45286         ctx.moveTo(x, y);
45287         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45288         this.is_empty = false;
45289     },
45290     
45291     clear: function()
45292     {
45293         var ctx = this.canvasElCtx();
45294         var canvas = this.canvasEl().dom;
45295         ctx.fillStyle = this.bg_color;
45296         ctx.clearRect(0, 0, canvas.width, canvas.height);
45297         ctx.fillRect(0, 0, canvas.width, canvas.height);
45298         this.curve_data = [];
45299         this.reset();
45300         this.is_empty = true;
45301     },
45302     
45303     fileEl: function()
45304     {
45305         return  this.el.select('input',true).first();
45306     },
45307     
45308     canvasEl: function()
45309     {
45310         return this.el.select('canvas',true).first();
45311     },
45312     
45313     canvasElCtx: function()
45314     {
45315         return this.el.select('canvas',true).first().dom.getContext('2d');
45316     },
45317     
45318     getImage: function(type)
45319     {
45320         if(this.is_empty) {
45321             return false;
45322         }
45323         
45324         // encryption ?
45325         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45326     },
45327     
45328     drawFromImage: function(img_src)
45329     {
45330         var img = new Image();
45331         
45332         img.onload = function(){
45333             this.canvasElCtx().drawImage(img, 0, 0);
45334         }.bind(this);
45335         
45336         img.src = img_src;
45337         
45338         this.is_empty = false;
45339     },
45340     
45341     selectImage: function()
45342     {
45343         this.fileEl().dom.click();
45344     },
45345     
45346     uploadImage: function(e)
45347     {
45348         var reader = new FileReader();
45349         
45350         reader.onload = function(e){
45351             var img = new Image();
45352             img.onload = function(){
45353                 this.reset();
45354                 this.canvasElCtx().drawImage(img, 0, 0);
45355             }.bind(this);
45356             img.src = e.target.result;
45357         }.bind(this);
45358         
45359         reader.readAsDataURL(e.target.files[0]);
45360     },
45361     
45362     // Bezier Point Constructor
45363     Point: (function () {
45364         function Point(x, y, time) {
45365             this.x = x;
45366             this.y = y;
45367             this.time = time || Date.now();
45368         }
45369         Point.prototype.distanceTo = function (start) {
45370             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45371         };
45372         Point.prototype.equals = function (other) {
45373             return this.x === other.x && this.y === other.y && this.time === other.time;
45374         };
45375         Point.prototype.velocityFrom = function (start) {
45376             return this.time !== start.time
45377             ? this.distanceTo(start) / (this.time - start.time)
45378             : 0;
45379         };
45380         return Point;
45381     }()),
45382     
45383     
45384     // Bezier Constructor
45385     Bezier: (function () {
45386         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45387             this.startPoint = startPoint;
45388             this.control2 = control2;
45389             this.control1 = control1;
45390             this.endPoint = endPoint;
45391             this.startWidth = startWidth;
45392             this.endWidth = endWidth;
45393         }
45394         Bezier.fromPoints = function (points, widths, scope) {
45395             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45396             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45397             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45398         };
45399         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45400             var dx1 = s1.x - s2.x;
45401             var dy1 = s1.y - s2.y;
45402             var dx2 = s2.x - s3.x;
45403             var dy2 = s2.y - s3.y;
45404             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45405             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45406             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45407             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45408             var dxm = m1.x - m2.x;
45409             var dym = m1.y - m2.y;
45410             var k = l2 / (l1 + l2);
45411             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45412             var tx = s2.x - cm.x;
45413             var ty = s2.y - cm.y;
45414             return {
45415                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45416                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45417             };
45418         };
45419         Bezier.prototype.length = function () {
45420             var steps = 10;
45421             var length = 0;
45422             var px;
45423             var py;
45424             for (var i = 0; i <= steps; i += 1) {
45425                 var t = i / steps;
45426                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45427                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45428                 if (i > 0) {
45429                     var xdiff = cx - px;
45430                     var ydiff = cy - py;
45431                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45432                 }
45433                 px = cx;
45434                 py = cy;
45435             }
45436             return length;
45437         };
45438         Bezier.prototype.point = function (t, start, c1, c2, end) {
45439             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45440             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45441             + (3.0 * c2 * (1.0 - t) * t * t)
45442             + (end * t * t * t);
45443         };
45444         return Bezier;
45445     }()),
45446     
45447     throttleStroke: function(fn, wait) {
45448       if (wait === void 0) { wait = 250; }
45449       var previous = 0;
45450       var timeout = null;
45451       var result;
45452       var storedContext;
45453       var storedArgs;
45454       var later = function () {
45455           previous = Date.now();
45456           timeout = null;
45457           result = fn.apply(storedContext, storedArgs);
45458           if (!timeout) {
45459               storedContext = null;
45460               storedArgs = [];
45461           }
45462       };
45463       return function wrapper() {
45464           var args = [];
45465           for (var _i = 0; _i < arguments.length; _i++) {
45466               args[_i] = arguments[_i];
45467           }
45468           var now = Date.now();
45469           var remaining = wait - (now - previous);
45470           storedContext = this;
45471           storedArgs = args;
45472           if (remaining <= 0 || remaining > wait) {
45473               if (timeout) {
45474                   clearTimeout(timeout);
45475                   timeout = null;
45476               }
45477               previous = now;
45478               result = fn.apply(storedContext, storedArgs);
45479               if (!timeout) {
45480                   storedContext = null;
45481                   storedArgs = [];
45482               }
45483           }
45484           else if (!timeout) {
45485               timeout = window.setTimeout(later, remaining);
45486           }
45487           return result;
45488       };
45489   }
45490   
45491 });
45492
45493  
45494
45495