sync
[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  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <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>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7423  * implemented by descendant classes.  This class should not be directly instantiated.
7424  * @constructor
7425  */
7426 Roo.grid.AbstractSelectionModel = function(){
7427     this.locked = false;
7428     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7429 };
7430
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7432     /** @ignore Called by the grid automatically. Do not call directly. */
7433     init : function(grid){
7434         this.grid = grid;
7435         this.initEvents();
7436     },
7437
7438     /**
7439      * Locks the selections.
7440      */
7441     lock : function(){
7442         this.locked = true;
7443     },
7444
7445     /**
7446      * Unlocks the selections.
7447      */
7448     unlock : function(){
7449         this.locked = false;
7450     },
7451
7452     /**
7453      * Returns true if the selections are locked.
7454      * @return {Boolean}
7455      */
7456     isLocked : function(){
7457         return this.locked;
7458     }
7459 });/*
7460  * Based on:
7461  * Ext JS Library 1.1.1
7462  * Copyright(c) 2006-2007, Ext JS, LLC.
7463  *
7464  * Originally Released Under LGPL - original licence link has changed is not relivant.
7465  *
7466  * Fork - LGPL
7467  * <script type="text/javascript">
7468  */
7469 /**
7470  * @extends Roo.grid.AbstractSelectionModel
7471  * @class Roo.grid.RowSelectionModel
7472  * The default SelectionModel used by {@link Roo.grid.Grid}.
7473  * It supports multiple selections and keyboard selection/navigation. 
7474  * @constructor
7475  * @param {Object} config
7476  */
7477 Roo.grid.RowSelectionModel = function(config){
7478     Roo.apply(this, config);
7479     this.selections = new Roo.util.MixedCollection(false, function(o){
7480         return o.id;
7481     });
7482
7483     this.last = false;
7484     this.lastActive = false;
7485
7486     this.addEvents({
7487         /**
7488         * @event selectionchange
7489         * Fires when the selection changes
7490         * @param {SelectionModel} this
7491         */
7492        "selectionchange" : true,
7493        /**
7494         * @event afterselectionchange
7495         * Fires after the selection changes (eg. by key press or clicking)
7496         * @param {SelectionModel} this
7497         */
7498        "afterselectionchange" : true,
7499        /**
7500         * @event beforerowselect
7501         * Fires when a row is selected being selected, return false to cancel.
7502         * @param {SelectionModel} this
7503         * @param {Number} rowIndex The selected index
7504         * @param {Boolean} keepExisting False if other selections will be cleared
7505         */
7506        "beforerowselect" : true,
7507        /**
7508         * @event rowselect
7509         * Fires when a row is selected.
7510         * @param {SelectionModel} this
7511         * @param {Number} rowIndex The selected index
7512         * @param {Roo.data.Record} r The record
7513         */
7514        "rowselect" : true,
7515        /**
7516         * @event rowdeselect
7517         * Fires when a row is deselected.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         */
7521         "rowdeselect" : true
7522     });
7523     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524     this.locked = false;
7525 };
7526
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7528     /**
7529      * @cfg {Boolean} singleSelect
7530      * True to allow selection of only one row at a time (defaults to false)
7531      */
7532     singleSelect : false,
7533
7534     // private
7535     initEvents : function(){
7536
7537         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538             this.grid.on("mousedown", this.handleMouseDown, this);
7539         }else{ // allow click to work like normal
7540             this.grid.on("rowclick", this.handleDragableRowClick, this);
7541         }
7542         // bootstrap does not have a view..
7543         var view = this.grid.view ? this.grid.view : this.grid;
7544         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7545             "up" : function(e){
7546                 if(!e.shiftKey){
7547                     this.selectPrevious(e.shiftKey);
7548                 }else if(this.last !== false && this.lastActive !== false){
7549                     var last = this.last;
7550                     this.selectRange(this.last,  this.lastActive-1);
7551                     view.focusRow(this.lastActive);
7552                     if(last !== false){
7553                         this.last = last;
7554                     }
7555                 }else{
7556                     this.selectFirstRow();
7557                 }
7558                 this.fireEvent("afterselectionchange", this);
7559             },
7560             "down" : function(e){
7561                 if(!e.shiftKey){
7562                     this.selectNext(e.shiftKey);
7563                 }else if(this.last !== false && this.lastActive !== false){
7564                     var last = this.last;
7565                     this.selectRange(this.last,  this.lastActive+1);
7566                     view.focusRow(this.lastActive);
7567                     if(last !== false){
7568                         this.last = last;
7569                     }
7570                 }else{
7571                     this.selectFirstRow();
7572                 }
7573                 this.fireEvent("afterselectionchange", this);
7574             },
7575             scope: this
7576         });
7577
7578          
7579         view.on("refresh", this.onRefresh, this);
7580         view.on("rowupdated", this.onRowUpdated, this);
7581         view.on("rowremoved", this.onRemove, this);
7582     },
7583
7584     // private
7585     onRefresh : function(){
7586         var ds = this.grid.ds, i, v = this.grid.view;
7587         var s = this.selections;
7588         s.each(function(r){
7589             if((i = ds.indexOfId(r.id)) != -1){
7590                 v.onRowSelect(i);
7591                 s.add(ds.getAt(i)); // updating the selection relate data
7592             }else{
7593                 s.remove(r);
7594             }
7595         });
7596     },
7597
7598     // private
7599     onRemove : function(v, index, r){
7600         this.selections.remove(r);
7601     },
7602
7603     // private
7604     onRowUpdated : function(v, index, r){
7605         if(this.isSelected(r)){
7606             v.onRowSelect(index);
7607         }
7608     },
7609
7610     /**
7611      * Select records.
7612      * @param {Array} records The records to select
7613      * @param {Boolean} keepExisting (optional) True to keep existing selections
7614      */
7615     selectRecords : function(records, keepExisting){
7616         if(!keepExisting){
7617             this.clearSelections();
7618         }
7619         var ds = this.grid.ds;
7620         for(var i = 0, len = records.length; i < len; i++){
7621             this.selectRow(ds.indexOf(records[i]), true);
7622         }
7623     },
7624
7625     /**
7626      * Gets the number of selected rows.
7627      * @return {Number}
7628      */
7629     getCount : function(){
7630         return this.selections.length;
7631     },
7632
7633     /**
7634      * Selects the first row in the grid.
7635      */
7636     selectFirstRow : function(){
7637         this.selectRow(0);
7638     },
7639
7640     /**
7641      * Select the last row.
7642      * @param {Boolean} keepExisting (optional) True to keep existing selections
7643      */
7644     selectLastRow : function(keepExisting){
7645         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7646     },
7647
7648     /**
7649      * Selects the row immediately following the last selected row.
7650      * @param {Boolean} keepExisting (optional) True to keep existing selections
7651      */
7652     selectNext : function(keepExisting){
7653         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654             this.selectRow(this.last+1, keepExisting);
7655             var view = this.grid.view ? this.grid.view : this.grid;
7656             view.focusRow(this.last);
7657         }
7658     },
7659
7660     /**
7661      * Selects the row that precedes the last selected row.
7662      * @param {Boolean} keepExisting (optional) True to keep existing selections
7663      */
7664     selectPrevious : function(keepExisting){
7665         if(this.last){
7666             this.selectRow(this.last-1, keepExisting);
7667             var view = this.grid.view ? this.grid.view : this.grid;
7668             view.focusRow(this.last);
7669         }
7670     },
7671
7672     /**
7673      * Returns the selected records
7674      * @return {Array} Array of selected records
7675      */
7676     getSelections : function(){
7677         return [].concat(this.selections.items);
7678     },
7679
7680     /**
7681      * Returns the first selected record.
7682      * @return {Record}
7683      */
7684     getSelected : function(){
7685         return this.selections.itemAt(0);
7686     },
7687
7688
7689     /**
7690      * Clears all selections.
7691      */
7692     clearSelections : function(fast){
7693         if(this.locked) {
7694             return;
7695         }
7696         if(fast !== true){
7697             var ds = this.grid.ds;
7698             var s = this.selections;
7699             s.each(function(r){
7700                 this.deselectRow(ds.indexOfId(r.id));
7701             }, this);
7702             s.clear();
7703         }else{
7704             this.selections.clear();
7705         }
7706         this.last = false;
7707     },
7708
7709
7710     /**
7711      * Selects all rows.
7712      */
7713     selectAll : function(){
7714         if(this.locked) {
7715             return;
7716         }
7717         this.selections.clear();
7718         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719             this.selectRow(i, true);
7720         }
7721     },
7722
7723     /**
7724      * Returns True if there is a selection.
7725      * @return {Boolean}
7726      */
7727     hasSelection : function(){
7728         return this.selections.length > 0;
7729     },
7730
7731     /**
7732      * Returns True if the specified row is selected.
7733      * @param {Number/Record} record The record or index of the record to check
7734      * @return {Boolean}
7735      */
7736     isSelected : function(index){
7737         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738         return (r && this.selections.key(r.id) ? true : false);
7739     },
7740
7741     /**
7742      * Returns True if the specified record id is selected.
7743      * @param {String} id The id of record to check
7744      * @return {Boolean}
7745      */
7746     isIdSelected : function(id){
7747         return (this.selections.key(id) ? true : false);
7748     },
7749
7750     // private
7751     handleMouseDown : function(e, t)
7752     {
7753         var view = this.grid.view ? this.grid.view : this.grid;
7754         var rowIndex;
7755         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7756             return;
7757         };
7758         if(e.shiftKey && this.last !== false){
7759             var last = this.last;
7760             this.selectRange(last, rowIndex, e.ctrlKey);
7761             this.last = last; // reset the last
7762             view.focusRow(rowIndex);
7763         }else{
7764             var isSelected = this.isSelected(rowIndex);
7765             if(e.button !== 0 && isSelected){
7766                 view.focusRow(rowIndex);
7767             }else if(e.ctrlKey && isSelected){
7768                 this.deselectRow(rowIndex);
7769             }else if(!isSelected){
7770                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771                 view.focusRow(rowIndex);
7772             }
7773         }
7774         this.fireEvent("afterselectionchange", this);
7775     },
7776     // private
7777     handleDragableRowClick :  function(grid, rowIndex, e) 
7778     {
7779         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780             this.selectRow(rowIndex, false);
7781             var view = this.grid.view ? this.grid.view : this.grid;
7782             view.focusRow(rowIndex);
7783              this.fireEvent("afterselectionchange", this);
7784         }
7785     },
7786     
7787     /**
7788      * Selects multiple rows.
7789      * @param {Array} rows Array of the indexes of the row to select
7790      * @param {Boolean} keepExisting (optional) True to keep existing selections
7791      */
7792     selectRows : function(rows, keepExisting){
7793         if(!keepExisting){
7794             this.clearSelections();
7795         }
7796         for(var i = 0, len = rows.length; i < len; i++){
7797             this.selectRow(rows[i], true);
7798         }
7799     },
7800
7801     /**
7802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803      * @param {Number} startRow The index of the first row in the range
7804      * @param {Number} endRow The index of the last row in the range
7805      * @param {Boolean} keepExisting (optional) True to retain existing selections
7806      */
7807     selectRange : function(startRow, endRow, keepExisting){
7808         if(this.locked) {
7809             return;
7810         }
7811         if(!keepExisting){
7812             this.clearSelections();
7813         }
7814         if(startRow <= endRow){
7815             for(var i = startRow; i <= endRow; i++){
7816                 this.selectRow(i, true);
7817             }
7818         }else{
7819             for(var i = startRow; i >= endRow; i--){
7820                 this.selectRow(i, true);
7821             }
7822         }
7823     },
7824
7825     /**
7826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827      * @param {Number} startRow The index of the first row in the range
7828      * @param {Number} endRow The index of the last row in the range
7829      */
7830     deselectRange : function(startRow, endRow, preventViewNotify){
7831         if(this.locked) {
7832             return;
7833         }
7834         for(var i = startRow; i <= endRow; i++){
7835             this.deselectRow(i, preventViewNotify);
7836         }
7837     },
7838
7839     /**
7840      * Selects a row.
7841      * @param {Number} row The index of the row to select
7842      * @param {Boolean} keepExisting (optional) True to keep existing selections
7843      */
7844     selectRow : function(index, keepExisting, preventViewNotify){
7845         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7846             return;
7847         }
7848         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849             if(!keepExisting || this.singleSelect){
7850                 this.clearSelections();
7851             }
7852             var r = this.grid.ds.getAt(index);
7853             this.selections.add(r);
7854             this.last = this.lastActive = index;
7855             if(!preventViewNotify){
7856                 var view = this.grid.view ? this.grid.view : this.grid;
7857                 view.onRowSelect(index);
7858             }
7859             this.fireEvent("rowselect", this, index, r);
7860             this.fireEvent("selectionchange", this);
7861         }
7862     },
7863
7864     /**
7865      * Deselects a row.
7866      * @param {Number} row The index of the row to deselect
7867      */
7868     deselectRow : function(index, preventViewNotify){
7869         if(this.locked) {
7870             return;
7871         }
7872         if(this.last == index){
7873             this.last = false;
7874         }
7875         if(this.lastActive == index){
7876             this.lastActive = false;
7877         }
7878         var r = this.grid.ds.getAt(index);
7879         this.selections.remove(r);
7880         if(!preventViewNotify){
7881             var view = this.grid.view ? this.grid.view : this.grid;
7882             view.onRowDeselect(index);
7883         }
7884         this.fireEvent("rowdeselect", this, index);
7885         this.fireEvent("selectionchange", this);
7886     },
7887
7888     // private
7889     restoreLast : function(){
7890         if(this._last){
7891             this.last = this._last;
7892         }
7893     },
7894
7895     // private
7896     acceptsNav : function(row, col, cm){
7897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7898     },
7899
7900     // private
7901     onEditorKey : function(field, e){
7902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7903         if(k == e.TAB){
7904             e.stopEvent();
7905             ed.completeEdit();
7906             if(e.shiftKey){
7907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908             }else{
7909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910             }
7911         }else if(k == e.ENTER && !e.ctrlKey){
7912             e.stopEvent();
7913             ed.completeEdit();
7914             if(e.shiftKey){
7915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916             }else{
7917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918             }
7919         }else if(k == e.ESC){
7920             ed.cancelEdit();
7921         }
7922         if(newCell){
7923             g.startEditing(newCell[0], newCell[1]);
7924         }
7925     }
7926 });/*
7927  * Based on:
7928  * Ext JS Library 1.1.1
7929  * Copyright(c) 2006-2007, Ext JS, LLC.
7930  *
7931  * Originally Released Under LGPL - original licence link has changed is not relivant.
7932  *
7933  * Fork - LGPL
7934  * <script type="text/javascript">
7935  */
7936  
7937
7938 /**
7939  * @class Roo.grid.ColumnModel
7940  * @extends Roo.util.Observable
7941  * This is the default implementation of a ColumnModel used by the Grid. It defines
7942  * the columns in the grid.
7943  * <br>Usage:<br>
7944  <pre><code>
7945  var colModel = new Roo.grid.ColumnModel([
7946         {header: "Ticker", width: 60, sortable: true, locked: true},
7947         {header: "Company Name", width: 150, sortable: true},
7948         {header: "Market Cap.", width: 100, sortable: true},
7949         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950         {header: "Employees", width: 100, sortable: true, resizable: false}
7951  ]);
7952  </code></pre>
7953  * <p>
7954  
7955  * The config options listed for this class are options which may appear in each
7956  * individual column definition.
7957  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958  * @constructor
7959  * @param {Object} config An Array of column config objects. See this class's
7960  * config objects for details.
7961 */
7962 Roo.grid.ColumnModel = function(config){
7963         /**
7964      * The config passed into the constructor
7965      */
7966     this.config = []; //config;
7967     this.lookup = {};
7968
7969     // if no id, create one
7970     // if the column does not have a dataIndex mapping,
7971     // map it to the order it is in the config
7972     for(var i = 0, len = config.length; i < len; i++){
7973         this.addColumn(config[i]);
7974         
7975     }
7976
7977     /**
7978      * The width of columns which have no width specified (defaults to 100)
7979      * @type Number
7980      */
7981     this.defaultWidth = 100;
7982
7983     /**
7984      * Default sortable of columns which have no sortable specified (defaults to false)
7985      * @type Boolean
7986      */
7987     this.defaultSortable = false;
7988
7989     this.addEvents({
7990         /**
7991              * @event widthchange
7992              * Fires when the width of a column changes.
7993              * @param {ColumnModel} this
7994              * @param {Number} columnIndex The column index
7995              * @param {Number} newWidth The new width
7996              */
7997             "widthchange": true,
7998         /**
7999              * @event headerchange
8000              * Fires when the text of a header changes.
8001              * @param {ColumnModel} this
8002              * @param {Number} columnIndex The column index
8003              * @param {Number} newText The new header text
8004              */
8005             "headerchange": true,
8006         /**
8007              * @event hiddenchange
8008              * Fires when a column is hidden or "unhidden".
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Boolean} hidden true if hidden, false otherwise
8012              */
8013             "hiddenchange": true,
8014             /**
8015          * @event columnmoved
8016          * Fires when a column is moved.
8017          * @param {ColumnModel} this
8018          * @param {Number} oldIndex
8019          * @param {Number} newIndex
8020          */
8021         "columnmoved" : true,
8022         /**
8023          * @event columlockchange
8024          * Fires when a column's locked state is changed
8025          * @param {ColumnModel} this
8026          * @param {Number} colIndex
8027          * @param {Boolean} locked true if locked
8028          */
8029         "columnlockchange" : true
8030     });
8031     Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 };
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034     /**
8035      * @cfg {String} header The header text to display in the Grid view.
8036      */
8037         /**
8038      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8039      */
8040         /**
8041      * @cfg {String} smHeader Header at Bootsrap Small width
8042      */
8043         /**
8044      * @cfg {String} mdHeader Header at Bootsrap Medium width
8045      */
8046         /**
8047      * @cfg {String} lgHeader Header at Bootsrap Large width
8048      */
8049         /**
8050      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8051      */
8052     /**
8053      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055      * specified, the column's index is used as an index into the Record's data Array.
8056      */
8057     /**
8058      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8060      */
8061     /**
8062      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063      * Defaults to the value of the {@link #defaultSortable} property.
8064      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8065      */
8066     /**
8067      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8068      */
8069     /**
8070      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8071      */
8072     /**
8073      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8074      */
8075     /**
8076      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8077      */
8078     /**
8079      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8083      */
8084        /**
8085      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8086      */
8087     /**
8088      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8089      */
8090     /**
8091      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8092      */
8093     /**
8094      * @cfg {String} cursor (Optional)
8095      */
8096     /**
8097      * @cfg {String} tooltip (Optional)
8098      */
8099     /**
8100      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8101      */
8102     /**
8103      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8104      */
8105     /**
8106      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111         /**
8112      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114     /**
8115      * Returns the id of the column at the specified index.
8116      * @param {Number} index The column index
8117      * @return {String} the id
8118      */
8119     getColumnId : function(index){
8120         return this.config[index].id;
8121     },
8122
8123     /**
8124      * Returns the column for a specified id.
8125      * @param {String} id The column id
8126      * @return {Object} the column
8127      */
8128     getColumnById : function(id){
8129         return this.lookup[id];
8130     },
8131
8132     
8133     /**
8134      * Returns the column Object for a specified dataIndex.
8135      * @param {String} dataIndex The column dataIndex
8136      * @return {Object|Boolean} the column or false if not found
8137      */
8138     getColumnByDataIndex: function(dataIndex){
8139         var index = this.findColumnIndex(dataIndex);
8140         return index > -1 ? this.config[index] : false;
8141     },
8142     
8143     /**
8144      * Returns the index for a specified column id.
8145      * @param {String} id The column id
8146      * @return {Number} the index, or -1 if not found
8147      */
8148     getIndexById : function(id){
8149         for(var i = 0, len = this.config.length; i < len; i++){
8150             if(this.config[i].id == id){
8151                 return i;
8152             }
8153         }
8154         return -1;
8155     },
8156     
8157     /**
8158      * Returns the index for a specified column dataIndex.
8159      * @param {String} dataIndex The column dataIndex
8160      * @return {Number} the index, or -1 if not found
8161      */
8162     
8163     findColumnIndex : function(dataIndex){
8164         for(var i = 0, len = this.config.length; i < len; i++){
8165             if(this.config[i].dataIndex == dataIndex){
8166                 return i;
8167             }
8168         }
8169         return -1;
8170     },
8171     
8172     
8173     moveColumn : function(oldIndex, newIndex){
8174         var c = this.config[oldIndex];
8175         this.config.splice(oldIndex, 1);
8176         this.config.splice(newIndex, 0, c);
8177         this.dataMap = null;
8178         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8179     },
8180
8181     isLocked : function(colIndex){
8182         return this.config[colIndex].locked === true;
8183     },
8184
8185     setLocked : function(colIndex, value, suppressEvent){
8186         if(this.isLocked(colIndex) == value){
8187             return;
8188         }
8189         this.config[colIndex].locked = value;
8190         if(!suppressEvent){
8191             this.fireEvent("columnlockchange", this, colIndex, value);
8192         }
8193     },
8194
8195     getTotalLockedWidth : function(){
8196         var totalWidth = 0;
8197         for(var i = 0; i < this.config.length; i++){
8198             if(this.isLocked(i) && !this.isHidden(i)){
8199                 this.totalWidth += this.getColumnWidth(i);
8200             }
8201         }
8202         return totalWidth;
8203     },
8204
8205     getLockedCount : function(){
8206         for(var i = 0, len = this.config.length; i < len; i++){
8207             if(!this.isLocked(i)){
8208                 return i;
8209             }
8210         }
8211         
8212         return this.config.length;
8213     },
8214
8215     /**
8216      * Returns the number of columns.
8217      * @return {Number}
8218      */
8219     getColumnCount : function(visibleOnly){
8220         if(visibleOnly === true){
8221             var c = 0;
8222             for(var i = 0, len = this.config.length; i < len; i++){
8223                 if(!this.isHidden(i)){
8224                     c++;
8225                 }
8226             }
8227             return c;
8228         }
8229         return this.config.length;
8230     },
8231
8232     /**
8233      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234      * @param {Function} fn
8235      * @param {Object} scope (optional)
8236      * @return {Array} result
8237      */
8238     getColumnsBy : function(fn, scope){
8239         var r = [];
8240         for(var i = 0, len = this.config.length; i < len; i++){
8241             var c = this.config[i];
8242             if(fn.call(scope||this, c, i) === true){
8243                 r[r.length] = c;
8244             }
8245         }
8246         return r;
8247     },
8248
8249     /**
8250      * Returns true if the specified column is sortable.
8251      * @param {Number} col The column index
8252      * @return {Boolean}
8253      */
8254     isSortable : function(col){
8255         if(typeof this.config[col].sortable == "undefined"){
8256             return this.defaultSortable;
8257         }
8258         return this.config[col].sortable;
8259     },
8260
8261     /**
8262      * Returns the rendering (formatting) function defined for the column.
8263      * @param {Number} col The column index.
8264      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265      */
8266     getRenderer : function(col){
8267         if(!this.config[col].renderer){
8268             return Roo.grid.ColumnModel.defaultRenderer;
8269         }
8270         return this.config[col].renderer;
8271     },
8272
8273     /**
8274      * Sets the rendering (formatting) function for a column.
8275      * @param {Number} col The column index
8276      * @param {Function} fn The function to use to process the cell's raw data
8277      * to return HTML markup for the grid view. The render function is called with
8278      * the following parameters:<ul>
8279      * <li>Data value.</li>
8280      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281      * <li>css A CSS style string to apply to the table cell.</li>
8282      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284      * <li>Row index</li>
8285      * <li>Column index</li>
8286      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287      */
8288     setRenderer : function(col, fn){
8289         this.config[col].renderer = fn;
8290     },
8291
8292     /**
8293      * Returns the width for the specified column.
8294      * @param {Number} col The column index
8295      * @param (optional) {String} gridSize bootstrap width size.
8296      * @return {Number}
8297      */
8298     getColumnWidth : function(col, gridSize)
8299         {
8300                 var cfg = this.config[col];
8301                 
8302                 if (typeof(gridSize) == 'undefined') {
8303                         return cfg.width * 1 || this.defaultWidth;
8304                 }
8305                 if (gridSize === false) { // if we set it..
8306                         return cfg.width || false;
8307                 }
8308                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309                 
8310                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8312                                 continue;
8313                         }
8314                         return cfg[ sizes[i] ];
8315                 }
8316                 return 1;
8317                 
8318     },
8319
8320     /**
8321      * Sets the width for a column.
8322      * @param {Number} col The column index
8323      * @param {Number} width The new width
8324      */
8325     setColumnWidth : function(col, width, suppressEvent){
8326         this.config[col].width = width;
8327         this.totalWidth = null;
8328         if(!suppressEvent){
8329              this.fireEvent("widthchange", this, col, width);
8330         }
8331     },
8332
8333     /**
8334      * Returns the total width of all columns.
8335      * @param {Boolean} includeHidden True to include hidden column widths
8336      * @return {Number}
8337      */
8338     getTotalWidth : function(includeHidden){
8339         if(!this.totalWidth){
8340             this.totalWidth = 0;
8341             for(var i = 0, len = this.config.length; i < len; i++){
8342                 if(includeHidden || !this.isHidden(i)){
8343                     this.totalWidth += this.getColumnWidth(i);
8344                 }
8345             }
8346         }
8347         return this.totalWidth;
8348     },
8349
8350     /**
8351      * Returns the header for the specified column.
8352      * @param {Number} col The column index
8353      * @return {String}
8354      */
8355     getColumnHeader : function(col){
8356         return this.config[col].header;
8357     },
8358
8359     /**
8360      * Sets the header for a column.
8361      * @param {Number} col The column index
8362      * @param {String} header The new header
8363      */
8364     setColumnHeader : function(col, header){
8365         this.config[col].header = header;
8366         this.fireEvent("headerchange", this, col, header);
8367     },
8368
8369     /**
8370      * Returns the tooltip for the specified column.
8371      * @param {Number} col The column index
8372      * @return {String}
8373      */
8374     getColumnTooltip : function(col){
8375             return this.config[col].tooltip;
8376     },
8377     /**
8378      * Sets the tooltip for a column.
8379      * @param {Number} col The column index
8380      * @param {String} tooltip The new tooltip
8381      */
8382     setColumnTooltip : function(col, tooltip){
8383             this.config[col].tooltip = tooltip;
8384     },
8385
8386     /**
8387      * Returns the dataIndex for the specified column.
8388      * @param {Number} col The column index
8389      * @return {Number}
8390      */
8391     getDataIndex : function(col){
8392         return this.config[col].dataIndex;
8393     },
8394
8395     /**
8396      * Sets the dataIndex for a column.
8397      * @param {Number} col The column index
8398      * @param {Number} dataIndex The new dataIndex
8399      */
8400     setDataIndex : function(col, dataIndex){
8401         this.config[col].dataIndex = dataIndex;
8402     },
8403
8404     
8405     
8406     /**
8407      * Returns true if the cell is editable.
8408      * @param {Number} colIndex The column index
8409      * @param {Number} rowIndex The row index - this is nto actually used..?
8410      * @return {Boolean}
8411      */
8412     isCellEditable : function(colIndex, rowIndex){
8413         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8414     },
8415
8416     /**
8417      * Returns the editor defined for the cell/column.
8418      * return false or null to disable editing.
8419      * @param {Number} colIndex The column index
8420      * @param {Number} rowIndex The row index
8421      * @return {Object}
8422      */
8423     getCellEditor : function(colIndex, rowIndex){
8424         return this.config[colIndex].editor;
8425     },
8426
8427     /**
8428      * Sets if a column is editable.
8429      * @param {Number} col The column index
8430      * @param {Boolean} editable True if the column is editable
8431      */
8432     setEditable : function(col, editable){
8433         this.config[col].editable = editable;
8434     },
8435
8436
8437     /**
8438      * Returns true if the column is hidden.
8439      * @param {Number} colIndex The column index
8440      * @return {Boolean}
8441      */
8442     isHidden : function(colIndex){
8443         return this.config[colIndex].hidden;
8444     },
8445
8446
8447     /**
8448      * Returns true if the column width cannot be changed
8449      */
8450     isFixed : function(colIndex){
8451         return this.config[colIndex].fixed;
8452     },
8453
8454     /**
8455      * Returns true if the column can be resized
8456      * @return {Boolean}
8457      */
8458     isResizable : function(colIndex){
8459         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8460     },
8461     /**
8462      * Sets if a column is hidden.
8463      * @param {Number} colIndex The column index
8464      * @param {Boolean} hidden True if the column is hidden
8465      */
8466     setHidden : function(colIndex, hidden){
8467         this.config[colIndex].hidden = hidden;
8468         this.totalWidth = null;
8469         this.fireEvent("hiddenchange", this, colIndex, hidden);
8470     },
8471
8472     /**
8473      * Sets the editor for a column.
8474      * @param {Number} col The column index
8475      * @param {Object} editor The editor object
8476      */
8477     setEditor : function(col, editor){
8478         this.config[col].editor = editor;
8479     },
8480     /**
8481      * Add a column (experimental...) - defaults to adding to the end..
8482      * @param {Object} config 
8483     */
8484     addColumn : function(c)
8485     {
8486     
8487         var i = this.config.length;
8488         this.config[i] = c;
8489         
8490         if(typeof c.dataIndex == "undefined"){
8491             c.dataIndex = i;
8492         }
8493         if(typeof c.renderer == "string"){
8494             c.renderer = Roo.util.Format[c.renderer];
8495         }
8496         if(typeof c.id == "undefined"){
8497             c.id = Roo.id();
8498         }
8499         if(c.editor && c.editor.xtype){
8500             c.editor  = Roo.factory(c.editor, Roo.grid);
8501         }
8502         if(c.editor && c.editor.isFormField){
8503             c.editor = new Roo.grid.GridEditor(c.editor);
8504         }
8505         this.lookup[c.id] = c;
8506     }
8507     
8508 });
8509
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 {
8512     if(typeof value == "object") {
8513         return value;
8514     }
8515         if(typeof value == "string" && value.length < 1){
8516             return "&#160;";
8517         }
8518     
8519         return String.format("{0}", value);
8520 };
8521
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8524 /*
8525  * Based on:
8526  * Ext JS Library 1.1.1
8527  * Copyright(c) 2006-2007, Ext JS, LLC.
8528  *
8529  * Originally Released Under LGPL - original licence link has changed is not relivant.
8530  *
8531  * Fork - LGPL
8532  * <script type="text/javascript">
8533  */
8534  
8535 /**
8536  * @class Roo.LoadMask
8537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8540  * element's UpdateManager load indicator and will be destroyed after the initial load.
8541  * @constructor
8542  * Create a new LoadMask
8543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544  * @param {Object} config The config object
8545  */
8546 Roo.LoadMask = function(el, config){
8547     this.el = Roo.get(el);
8548     Roo.apply(this, config);
8549     if(this.store){
8550         this.store.on('beforeload', this.onBeforeLoad, this);
8551         this.store.on('load', this.onLoad, this);
8552         this.store.on('loadexception', this.onLoadException, this);
8553         this.removeMask = false;
8554     }else{
8555         var um = this.el.getUpdateManager();
8556         um.showLoadIndicator = false; // disable the default indicator
8557         um.on('beforeupdate', this.onBeforeLoad, this);
8558         um.on('update', this.onLoad, this);
8559         um.on('failure', this.onLoad, this);
8560         this.removeMask = true;
8561     }
8562 };
8563
8564 Roo.LoadMask.prototype = {
8565     /**
8566      * @cfg {Boolean} removeMask
8567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8569      */
8570     removeMask : false,
8571     /**
8572      * @cfg {String} msg
8573      * The text to display in a centered loading message box (defaults to 'Loading...')
8574      */
8575     msg : 'Loading...',
8576     /**
8577      * @cfg {String} msgCls
8578      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8579      */
8580     msgCls : 'x-mask-loading',
8581
8582     /**
8583      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8584      * @type Boolean
8585      */
8586     disabled: false,
8587
8588     /**
8589      * Disables the mask to prevent it from being displayed
8590      */
8591     disable : function(){
8592        this.disabled = true;
8593     },
8594
8595     /**
8596      * Enables the mask so that it can be displayed
8597      */
8598     enable : function(){
8599         this.disabled = false;
8600     },
8601     
8602     onLoadException : function()
8603     {
8604         Roo.log(arguments);
8605         
8606         if (typeof(arguments[3]) != 'undefined') {
8607             Roo.MessageBox.alert("Error loading",arguments[3]);
8608         } 
8609         /*
8610         try {
8611             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8612                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8613             }   
8614         } catch(e) {
8615             
8616         }
8617         */
8618     
8619         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8620     },
8621     // private
8622     onLoad : function()
8623     {
8624         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8625     },
8626
8627     // private
8628     onBeforeLoad : function(){
8629         if(!this.disabled){
8630             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8631         }
8632     },
8633
8634     // private
8635     destroy : function(){
8636         if(this.store){
8637             this.store.un('beforeload', this.onBeforeLoad, this);
8638             this.store.un('load', this.onLoad, this);
8639             this.store.un('loadexception', this.onLoadException, this);
8640         }else{
8641             var um = this.el.getUpdateManager();
8642             um.un('beforeupdate', this.onBeforeLoad, this);
8643             um.un('update', this.onLoad, this);
8644             um.un('failure', this.onLoad, this);
8645         }
8646     }
8647 };/**
8648  * @class Roo.bootstrap.Table
8649  * @licence LGBL
8650  * @extends Roo.bootstrap.Component
8651  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8652  * Similar to Roo.grid.Grid
8653  * <pre><code>
8654  var table = Roo.factory({
8655     xtype : 'Table',
8656     xns : Roo.bootstrap,
8657     autoSizeColumns: true,
8658     
8659     
8660     store : {
8661         xtype : 'Store',
8662         xns : Roo.data,
8663         remoteSort : true,
8664         sortInfo : { direction : 'ASC', field: 'name' },
8665         proxy : {
8666            xtype : 'HttpProxy',
8667            xns : Roo.data,
8668            method : 'GET',
8669            url : 'https://example.com/some.data.url.json'
8670         },
8671         reader : {
8672            xtype : 'JsonReader',
8673            xns : Roo.data,
8674            fields : [ 'id', 'name', whatever' ],
8675            id : 'id',
8676            root : 'data'
8677         }
8678     },
8679     cm : [
8680         {
8681             xtype : 'ColumnModel',
8682             xns : Roo.grid,
8683             align : 'center',
8684             cursor : 'pointer',
8685             dataIndex : 'is_in_group',
8686             header : "Name",
8687             sortable : true,
8688             renderer : function(v, x , r) {  
8689             
8690                 return String.format("{0}", v)
8691             }
8692             width : 3
8693         } // more columns..
8694     ],
8695     selModel : {
8696         xtype : 'RowSelectionModel',
8697         xns : Roo.bootstrap.Table
8698         // you can add listeners to catch selection change here....
8699     }
8700      
8701
8702  });
8703  // set any options
8704  grid.render(Roo.get("some-div"));
8705 </code></pre>
8706
8707 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8708
8709
8710
8711  *
8712  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8713  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8714  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8715  * 
8716  * @cfg {String} cls table class
8717  *
8718  * 
8719  * @cfg {boolean} striped Should the rows be alternative striped
8720  * @cfg {boolean} bordered Add borders to the table
8721  * @cfg {boolean} hover Add hover highlighting
8722  * @cfg {boolean} condensed Format condensed
8723  * @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,
8724  *                also adds table-responsive (see bootstrap docs for details)
8725  * @cfg {Boolean} loadMask (true|false) default false
8726  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8727  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8728  * @cfg {Boolean} rowSelection (true|false) default false
8729  * @cfg {Boolean} cellSelection (true|false) default false
8730  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8731  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8732  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8733  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8734  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8735  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8736  * 
8737  * @constructor
8738  * Create a new Table
8739  * @param {Object} config The config object
8740  */
8741
8742 Roo.bootstrap.Table = function(config)
8743 {
8744     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8745      
8746     // BC...
8747     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8748     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8749     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8750     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8751     
8752     this.view = this; // compat with grid.
8753     
8754     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8755     if (this.sm) {
8756         this.sm.grid = this;
8757         this.selModel = Roo.factory(this.sm, Roo.grid);
8758         this.sm = this.selModel;
8759         this.sm.xmodule = this.xmodule || false;
8760     }
8761     
8762     if (this.cm && typeof(this.cm.config) == 'undefined') {
8763         this.colModel = new Roo.grid.ColumnModel(this.cm);
8764         this.cm = this.colModel;
8765         this.cm.xmodule = this.xmodule || false;
8766     }
8767     if (this.store) {
8768         this.store= Roo.factory(this.store, Roo.data);
8769         this.ds = this.store;
8770         this.ds.xmodule = this.xmodule || false;
8771          
8772     }
8773     if (this.footer && this.store) {
8774         this.footer.dataSource = this.ds;
8775         this.footer = Roo.factory(this.footer);
8776     }
8777     
8778     /** @private */
8779     this.addEvents({
8780         /**
8781          * @event cellclick
8782          * Fires when a cell is clicked
8783          * @param {Roo.bootstrap.Table} this
8784          * @param {Roo.Element} el
8785          * @param {Number} rowIndex
8786          * @param {Number} columnIndex
8787          * @param {Roo.EventObject} e
8788          */
8789         "cellclick" : true,
8790         /**
8791          * @event celldblclick
8792          * Fires when a cell is double clicked
8793          * @param {Roo.bootstrap.Table} this
8794          * @param {Roo.Element} el
8795          * @param {Number} rowIndex
8796          * @param {Number} columnIndex
8797          * @param {Roo.EventObject} e
8798          */
8799         "celldblclick" : true,
8800         /**
8801          * @event rowclick
8802          * Fires when a row is clicked
8803          * @param {Roo.bootstrap.Table} this
8804          * @param {Roo.Element} el
8805          * @param {Number} rowIndex
8806          * @param {Roo.EventObject} e
8807          */
8808         "rowclick" : true,
8809         /**
8810          * @event rowdblclick
8811          * Fires when a row is double clicked
8812          * @param {Roo.bootstrap.Table} this
8813          * @param {Roo.Element} el
8814          * @param {Number} rowIndex
8815          * @param {Roo.EventObject} e
8816          */
8817         "rowdblclick" : true,
8818         /**
8819          * @event mouseover
8820          * Fires when a mouseover occur
8821          * @param {Roo.bootstrap.Table} this
8822          * @param {Roo.Element} el
8823          * @param {Number} rowIndex
8824          * @param {Number} columnIndex
8825          * @param {Roo.EventObject} e
8826          */
8827         "mouseover" : true,
8828         /**
8829          * @event mouseout
8830          * Fires when a mouseout occur
8831          * @param {Roo.bootstrap.Table} this
8832          * @param {Roo.Element} el
8833          * @param {Number} rowIndex
8834          * @param {Number} columnIndex
8835          * @param {Roo.EventObject} e
8836          */
8837         "mouseout" : true,
8838         /**
8839          * @event rowclass
8840          * Fires when a row is rendered, so you can change add a style to it.
8841          * @param {Roo.bootstrap.Table} this
8842          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8843          */
8844         'rowclass' : true,
8845           /**
8846          * @event rowsrendered
8847          * Fires when all the  rows have been rendered
8848          * @param {Roo.bootstrap.Table} this
8849          */
8850         'rowsrendered' : true,
8851         /**
8852          * @event contextmenu
8853          * The raw contextmenu event for the entire grid.
8854          * @param {Roo.EventObject} e
8855          */
8856         "contextmenu" : true,
8857         /**
8858          * @event rowcontextmenu
8859          * Fires when a row is right clicked
8860          * @param {Roo.bootstrap.Table} this
8861          * @param {Number} rowIndex
8862          * @param {Roo.EventObject} e
8863          */
8864         "rowcontextmenu" : true,
8865         /**
8866          * @event cellcontextmenu
8867          * Fires when a cell is right clicked
8868          * @param {Roo.bootstrap.Table} this
8869          * @param {Number} rowIndex
8870          * @param {Number} cellIndex
8871          * @param {Roo.EventObject} e
8872          */
8873          "cellcontextmenu" : true,
8874          /**
8875          * @event headercontextmenu
8876          * Fires when a header is right clicked
8877          * @param {Roo.bootstrap.Table} this
8878          * @param {Number} columnIndex
8879          * @param {Roo.EventObject} e
8880          */
8881         "headercontextmenu" : true,
8882         /**
8883          * @event mousedown
8884          * The raw mousedown event for the entire grid.
8885          * @param {Roo.EventObject} e
8886          */
8887         "mousedown" : true
8888         
8889     });
8890 };
8891
8892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8893     
8894     cls: false,
8895     
8896     striped : false,
8897     scrollBody : false,
8898     bordered: false,
8899     hover:  false,
8900     condensed : false,
8901     responsive : false,
8902     sm : false,
8903     cm : false,
8904     store : false,
8905     loadMask : false,
8906     footerShow : true,
8907     headerShow : true,
8908     enableColumnResize: true,
8909   
8910     rowSelection : false,
8911     cellSelection : false,
8912     layout : false,
8913
8914     minColumnWidth : 50,
8915     
8916     // Roo.Element - the tbody
8917     bodyEl: false,  // <tbody> Roo.Element - thead element    
8918     headEl: false,  // <thead> Roo.Element - thead element
8919     resizeProxy : false, // proxy element for dragging?
8920
8921
8922     
8923     container: false, // used by gridpanel...
8924     
8925     lazyLoad : false,
8926     
8927     CSS : Roo.util.CSS,
8928     
8929     auto_hide_footer : false,
8930     
8931     view: false, // actually points to this..
8932     
8933     getAutoCreate : function()
8934     {
8935         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8936         
8937         cfg = {
8938             tag: 'table',
8939             cls : 'table', 
8940             cn : []
8941         };
8942         // this get's auto added by panel.Grid
8943         if (this.scrollBody) {
8944             cfg.cls += ' table-body-fixed';
8945         }    
8946         if (this.striped) {
8947             cfg.cls += ' table-striped';
8948         }
8949         
8950         if (this.hover) {
8951             cfg.cls += ' table-hover';
8952         }
8953         if (this.bordered) {
8954             cfg.cls += ' table-bordered';
8955         }
8956         if (this.condensed) {
8957             cfg.cls += ' table-condensed';
8958         }
8959         
8960         if (this.responsive) {
8961             cfg.cls += ' table-responsive';
8962         }
8963         
8964         if (this.cls) {
8965             cfg.cls+=  ' ' +this.cls;
8966         }
8967         
8968         
8969         
8970         if (this.layout) {
8971             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8972         }
8973         
8974         if(this.store || this.cm){
8975             if(this.headerShow){
8976                 cfg.cn.push(this.renderHeader());
8977             }
8978             
8979             cfg.cn.push(this.renderBody());
8980             
8981             if(this.footerShow){
8982                 cfg.cn.push(this.renderFooter());
8983             }
8984             // where does this come from?
8985             //cfg.cls+=  ' TableGrid';
8986         }
8987         
8988         return { cn : [ cfg ] };
8989     },
8990     
8991     initEvents : function()
8992     {   
8993         if(!this.store || !this.cm){
8994             return;
8995         }
8996         if (this.selModel) {
8997             this.selModel.initEvents();
8998         }
8999         
9000         
9001         //Roo.log('initEvents with ds!!!!');
9002         
9003         this.bodyEl = this.el.select('tbody', true).first();
9004         this.headEl = this.el.select('thead', true).first();
9005         this.mainFoot = this.el.select('tfoot', true).first();
9006         
9007         
9008         
9009         
9010         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9011             e.on('click', this.sort, this);
9012         }, this);
9013         
9014         
9015         // why is this done????? = it breaks dialogs??
9016         //this.parent().el.setStyle('position', 'relative');
9017         
9018         
9019         if (this.footer) {
9020             this.footer.parentId = this.id;
9021             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9022             
9023             if(this.lazyLoad){
9024                 this.el.select('tfoot tr td').first().addClass('hide');
9025             }
9026         } 
9027         
9028         if(this.loadMask) {
9029             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9030         }
9031         
9032         this.store.on('load', this.onLoad, this);
9033         this.store.on('beforeload', this.onBeforeLoad, this);
9034         this.store.on('update', this.onUpdate, this);
9035         this.store.on('add', this.onAdd, this);
9036         this.store.on("clear", this.clear, this);
9037         
9038         this.el.on("contextmenu", this.onContextMenu, this);
9039         
9040         
9041         this.cm.on("headerchange", this.onHeaderChange, this);
9042         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9043
9044  //?? does bodyEl get replaced on render?
9045         this.bodyEl.on("click", this.onClick, this);
9046         this.bodyEl.on("dblclick", this.onDblClick, this);        
9047         this.bodyEl.on('scroll', this.onBodyScroll, this);
9048
9049         // guessing mainbody will work - this relays usually caught by selmodel at present.
9050         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9051   
9052   
9053         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9054         
9055   
9056         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9057             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9058         }
9059         
9060         this.initCSS();
9061     },
9062     // Compatibility with grid - we implement all the view features at present.
9063     getView : function()
9064     {
9065         return this;
9066     },
9067     
9068     initCSS : function()
9069     {
9070         
9071         
9072         var cm = this.cm, styles = [];
9073         this.CSS.removeStyleSheet(this.id + '-cssrules');
9074         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9075         // we can honour xs/sm/md/xl  as widths...
9076         // we first have to decide what widht we are currently at...
9077         var sz = Roo.getGridSize();
9078         
9079         var total = 0;
9080         var last = -1;
9081         var cols = []; // visable cols.
9082         var total_abs = 0;
9083         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9084             var w = cm.getColumnWidth(i, false);
9085             if(cm.isHidden(i)){
9086                 cols.push( { rel : false, abs : 0 });
9087                 continue;
9088             }
9089             if (w !== false) {
9090                 cols.push( { rel : false, abs : w });
9091                 total_abs += w;
9092                 last = i; // not really..
9093                 continue;
9094             }
9095             var w = cm.getColumnWidth(i, sz);
9096             if (w > 0) {
9097                 last = i
9098             }
9099             total += w;
9100             cols.push( { rel : w, abs : false });
9101         }
9102         
9103         var avail = this.bodyEl.dom.clientWidth - total_abs;
9104         
9105         var unitWidth = Math.floor(avail / total);
9106         var rem = avail - (unitWidth * total);
9107         
9108         var hidden, width, pos = 0 , splithide , left;
9109         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9110             
9111             hidden = 'display:none;';
9112             left = '';
9113             width  = 'width:0px;';
9114             splithide = '';
9115             if(!cm.isHidden(i)){
9116                 hidden = '';
9117                 
9118                 
9119                 // we can honour xs/sm/md/xl ?
9120                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9121                 if (w===0) {
9122                     hidden = 'display:none;';
9123                 }
9124                 // width should return a small number...
9125                 if (i == last) {
9126                     w+=rem; // add the remaining with..
9127                 }
9128                 pos += w;
9129                 left = "left:" + (pos -4) + "px;";
9130                 width = "width:" + w+ "px;";
9131                 
9132             }
9133             if (this.responsive) {
9134                 width = '';
9135                 left = '';
9136                 hidden = cm.isHidden(i) ? 'display:none' : '';
9137                 splithide = 'display: none';
9138             }
9139             
9140             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9141             if (this.headEl) {
9142                 if (i == last) {
9143                     splithide = 'display:none;';
9144                 }
9145                 
9146                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9147                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9148                 );
9149             }
9150             
9151         }
9152         //Roo.log(styles.join(''));
9153         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9154         
9155     },
9156     
9157     
9158     
9159     onContextMenu : function(e, t)
9160     {
9161         this.processEvent("contextmenu", e);
9162     },
9163     
9164     processEvent : function(name, e)
9165     {
9166         if (name != 'touchstart' ) {
9167             this.fireEvent(name, e);    
9168         }
9169         
9170         var t = e.getTarget();
9171         
9172         var cell = Roo.get(t);
9173         
9174         if(!cell){
9175             return;
9176         }
9177         
9178         if(cell.findParent('tfoot', false, true)){
9179             return;
9180         }
9181         
9182         if(cell.findParent('thead', false, true)){
9183             
9184             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9185                 cell = Roo.get(t).findParent('th', false, true);
9186                 if (!cell) {
9187                     Roo.log("failed to find th in thead?");
9188                     Roo.log(e.getTarget());
9189                     return;
9190                 }
9191             }
9192             
9193             var cellIndex = cell.dom.cellIndex;
9194             
9195             var ename = name == 'touchstart' ? 'click' : name;
9196             this.fireEvent("header" + ename, this, cellIndex, e);
9197             
9198             return;
9199         }
9200         
9201         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9202             cell = Roo.get(t).findParent('td', false, true);
9203             if (!cell) {
9204                 Roo.log("failed to find th in tbody?");
9205                 Roo.log(e.getTarget());
9206                 return;
9207             }
9208         }
9209         
9210         var row = cell.findParent('tr', false, true);
9211         var cellIndex = cell.dom.cellIndex;
9212         var rowIndex = row.dom.rowIndex - 1;
9213         
9214         if(row !== false){
9215             
9216             this.fireEvent("row" + name, this, rowIndex, e);
9217             
9218             if(cell !== false){
9219             
9220                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9221             }
9222         }
9223         
9224     },
9225     
9226     onMouseover : function(e, el)
9227     {
9228         var cell = Roo.get(el);
9229         
9230         if(!cell){
9231             return;
9232         }
9233         
9234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9235             cell = cell.findParent('td', false, true);
9236         }
9237         
9238         var row = cell.findParent('tr', false, true);
9239         var cellIndex = cell.dom.cellIndex;
9240         var rowIndex = row.dom.rowIndex - 1; // start from 0
9241         
9242         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9243         
9244     },
9245     
9246     onMouseout : function(e, el)
9247     {
9248         var cell = Roo.get(el);
9249         
9250         if(!cell){
9251             return;
9252         }
9253         
9254         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9255             cell = cell.findParent('td', false, true);
9256         }
9257         
9258         var row = cell.findParent('tr', false, true);
9259         var cellIndex = cell.dom.cellIndex;
9260         var rowIndex = row.dom.rowIndex - 1; // start from 0
9261         
9262         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9263         
9264     },
9265     
9266     onClick : function(e, el)
9267     {
9268         var cell = Roo.get(el);
9269         
9270         if(!cell || (!this.cellSelection && !this.rowSelection)){
9271             return;
9272         }
9273         
9274         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9275             cell = cell.findParent('td', false, true);
9276         }
9277         
9278         if(!cell || typeof(cell) == 'undefined'){
9279             return;
9280         }
9281         
9282         var row = cell.findParent('tr', false, true);
9283         
9284         if(!row || typeof(row) == 'undefined'){
9285             return;
9286         }
9287         
9288         var cellIndex = cell.dom.cellIndex;
9289         var rowIndex = this.getRowIndex(row);
9290         
9291         // why??? - should these not be based on SelectionModel?
9292         //if(this.cellSelection){
9293             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9294         //}
9295         
9296         //if(this.rowSelection){
9297             this.fireEvent('rowclick', this, row, rowIndex, e);
9298         //}
9299          
9300     },
9301         
9302     onDblClick : function(e,el)
9303     {
9304         var cell = Roo.get(el);
9305         
9306         if(!cell || (!this.cellSelection && !this.rowSelection)){
9307             return;
9308         }
9309         
9310         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9311             cell = cell.findParent('td', false, true);
9312         }
9313         
9314         if(!cell || typeof(cell) == 'undefined'){
9315             return;
9316         }
9317         
9318         var row = cell.findParent('tr', false, true);
9319         
9320         if(!row || typeof(row) == 'undefined'){
9321             return;
9322         }
9323         
9324         var cellIndex = cell.dom.cellIndex;
9325         var rowIndex = this.getRowIndex(row);
9326         
9327         if(this.cellSelection){
9328             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9329         }
9330         
9331         if(this.rowSelection){
9332             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9333         }
9334     },
9335     findRowIndex : function(el)
9336     {
9337         var cell = Roo.get(el);
9338         if(!cell) {
9339             return false;
9340         }
9341         var row = cell.findParent('tr', false, true);
9342         
9343         if(!row || typeof(row) == 'undefined'){
9344             return false;
9345         }
9346         return this.getRowIndex(row);
9347     },
9348     sort : function(e,el)
9349     {
9350         var col = Roo.get(el);
9351         
9352         if(!col.hasClass('sortable')){
9353             return;
9354         }
9355         
9356         var sort = col.attr('sort');
9357         var dir = 'ASC';
9358         
9359         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9360             dir = 'DESC';
9361         }
9362         
9363         this.store.sortInfo = {field : sort, direction : dir};
9364         
9365         if (this.footer) {
9366             Roo.log("calling footer first");
9367             this.footer.onClick('first');
9368         } else {
9369         
9370             this.store.load({ params : { start : 0 } });
9371         }
9372     },
9373     
9374     renderHeader : function()
9375     {
9376         var header = {
9377             tag: 'thead',
9378             cn : []
9379         };
9380         
9381         var cm = this.cm;
9382         this.totalWidth = 0;
9383         
9384         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9385             
9386             var config = cm.config[i];
9387             
9388             var c = {
9389                 tag: 'th',
9390                 cls : 'x-hcol-' + i,
9391                 style : '',
9392                 
9393                 html: cm.getColumnHeader(i)
9394             };
9395             
9396             var tooltip = cm.getColumnTooltip(i);
9397             if (tooltip) {
9398                 c.tooltip = tooltip;
9399             }
9400             
9401             
9402             var hh = '';
9403             
9404             if(typeof(config.sortable) != 'undefined' && config.sortable){
9405                 c.cls += ' sortable';
9406                 c.html = '<i class="fa"></i>' + c.html;
9407             }
9408             
9409             // could use BS4 hidden-..-down 
9410             
9411             if(typeof(config.lgHeader) != 'undefined'){
9412                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9413             }
9414             
9415             if(typeof(config.mdHeader) != 'undefined'){
9416                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9417             }
9418             
9419             if(typeof(config.smHeader) != 'undefined'){
9420                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9421             }
9422             
9423             if(typeof(config.xsHeader) != 'undefined'){
9424                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9425             }
9426             
9427             if(hh.length){
9428                 c.html = hh;
9429             }
9430             
9431             if(typeof(config.tooltip) != 'undefined'){
9432                 c.tooltip = config.tooltip;
9433             }
9434             
9435             if(typeof(config.colspan) != 'undefined'){
9436                 c.colspan = config.colspan;
9437             }
9438             
9439             // hidden is handled by CSS now
9440             
9441             if(typeof(config.dataIndex) != 'undefined'){
9442                 c.sort = config.dataIndex;
9443             }
9444             
9445            
9446             
9447             if(typeof(config.align) != 'undefined' && config.align.length){
9448                 c.style += ' text-align:' + config.align + ';';
9449             }
9450             
9451             /* width is done in CSS
9452              *if(typeof(config.width) != 'undefined'){
9453                 c.style += ' width:' + config.width + 'px;';
9454                 this.totalWidth += config.width;
9455             } else {
9456                 this.totalWidth += 100; // assume minimum of 100 per column?
9457             }
9458             */
9459             
9460             if(typeof(config.cls) != 'undefined'){
9461                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9462             }
9463             // this is the bit that doesnt reall work at all...
9464             
9465             if (this.responsive) {
9466                  
9467             
9468                 ['xs','sm','md','lg'].map(function(size){
9469                     
9470                     if(typeof(config[size]) == 'undefined'){
9471                         return;
9472                     }
9473                      
9474                     if (!config[size]) { // 0 = hidden
9475                         // BS 4 '0' is treated as hide that column and below.
9476                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9477                         return;
9478                     }
9479                     
9480                     c.cls += ' col-' + size + '-' + config[size] + (
9481                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9482                     );
9483                     
9484                     
9485                 });
9486             }
9487             // at the end?
9488             
9489             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9490             
9491             
9492             
9493             
9494             header.cn.push(c)
9495         }
9496         
9497         return header;
9498     },
9499     
9500     renderBody : function()
9501     {
9502         var body = {
9503             tag: 'tbody',
9504             cn : [
9505                 {
9506                     tag: 'tr',
9507                     cn : [
9508                         {
9509                             tag : 'td',
9510                             colspan :  this.cm.getColumnCount()
9511                         }
9512                     ]
9513                 }
9514             ]
9515         };
9516         
9517         return body;
9518     },
9519     
9520     renderFooter : function()
9521     {
9522         var footer = {
9523             tag: 'tfoot',
9524             cn : [
9525                 {
9526                     tag: 'tr',
9527                     cn : [
9528                         {
9529                             tag : 'td',
9530                             colspan :  this.cm.getColumnCount()
9531                         }
9532                     ]
9533                 }
9534             ]
9535         };
9536         
9537         return footer;
9538     },
9539     
9540     
9541     
9542     onLoad : function()
9543     {
9544 //        Roo.log('ds onload');
9545         this.clear();
9546         
9547         var _this = this;
9548         var cm = this.cm;
9549         var ds = this.store;
9550         
9551         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9552             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9553             if (_this.store.sortInfo) {
9554                     
9555                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9556                     e.select('i', true).addClass(['fa-arrow-up']);
9557                 }
9558                 
9559                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9560                     e.select('i', true).addClass(['fa-arrow-down']);
9561                 }
9562             }
9563         });
9564         
9565         var tbody =  this.bodyEl;
9566               
9567         if(ds.getCount() > 0){
9568             ds.data.each(function(d,rowIndex){
9569                 var row =  this.renderRow(cm, ds, rowIndex);
9570                 
9571                 tbody.createChild(row);
9572                 
9573                 var _this = this;
9574                 
9575                 if(row.cellObjects.length){
9576                     Roo.each(row.cellObjects, function(r){
9577                         _this.renderCellObject(r);
9578                     })
9579                 }
9580                 
9581             }, this);
9582         }
9583         
9584         var tfoot = this.el.select('tfoot', true).first();
9585         
9586         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9587             
9588             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9589             
9590             var total = this.ds.getTotalCount();
9591             
9592             if(this.footer.pageSize < total){
9593                 this.mainFoot.show();
9594             }
9595         }
9596         
9597         Roo.each(this.el.select('tbody td', true).elements, function(e){
9598             e.on('mouseover', _this.onMouseover, _this);
9599         });
9600         
9601         Roo.each(this.el.select('tbody td', true).elements, function(e){
9602             e.on('mouseout', _this.onMouseout, _this);
9603         });
9604         this.fireEvent('rowsrendered', this);
9605         
9606         this.autoSize();
9607         
9608         this.initCSS(); /// resize cols
9609
9610         
9611     },
9612     
9613     
9614     onUpdate : function(ds,record)
9615     {
9616         this.refreshRow(record);
9617         this.autoSize();
9618     },
9619     
9620     onRemove : function(ds, record, index, isUpdate){
9621         if(isUpdate !== true){
9622             this.fireEvent("beforerowremoved", this, index, record);
9623         }
9624         var bt = this.bodyEl.dom;
9625         
9626         var rows = this.el.select('tbody > tr', true).elements;
9627         
9628         if(typeof(rows[index]) != 'undefined'){
9629             bt.removeChild(rows[index].dom);
9630         }
9631         
9632 //        if(bt.rows[index]){
9633 //            bt.removeChild(bt.rows[index]);
9634 //        }
9635         
9636         if(isUpdate !== true){
9637             //this.stripeRows(index);
9638             //this.syncRowHeights(index, index);
9639             //this.layout();
9640             this.fireEvent("rowremoved", this, index, record);
9641         }
9642     },
9643     
9644     onAdd : function(ds, records, rowIndex)
9645     {
9646         //Roo.log('on Add called');
9647         // - note this does not handle multiple adding very well..
9648         var bt = this.bodyEl.dom;
9649         for (var i =0 ; i < records.length;i++) {
9650             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9651             //Roo.log(records[i]);
9652             //Roo.log(this.store.getAt(rowIndex+i));
9653             this.insertRow(this.store, rowIndex + i, false);
9654             return;
9655         }
9656         
9657     },
9658     
9659     
9660     refreshRow : function(record){
9661         var ds = this.store, index;
9662         if(typeof record == 'number'){
9663             index = record;
9664             record = ds.getAt(index);
9665         }else{
9666             index = ds.indexOf(record);
9667             if (index < 0) {
9668                 return; // should not happen - but seems to 
9669             }
9670         }
9671         this.insertRow(ds, index, true);
9672         this.autoSize();
9673         this.onRemove(ds, record, index+1, true);
9674         this.autoSize();
9675         //this.syncRowHeights(index, index);
9676         //this.layout();
9677         this.fireEvent("rowupdated", this, index, record);
9678     },
9679     // private - called by RowSelection
9680     onRowSelect : function(rowIndex){
9681         var row = this.getRowDom(rowIndex);
9682         row.addClass(['bg-info','info']);
9683     },
9684     // private - called by RowSelection
9685     onRowDeselect : function(rowIndex)
9686     {
9687         if (rowIndex < 0) {
9688             return;
9689         }
9690         var row = this.getRowDom(rowIndex);
9691         row.removeClass(['bg-info','info']);
9692     },
9693       /**
9694      * Focuses the specified row.
9695      * @param {Number} row The row index
9696      */
9697     focusRow : function(row)
9698     {
9699         //Roo.log('GridView.focusRow');
9700         var x = this.bodyEl.dom.scrollLeft;
9701         this.focusCell(row, 0, false);
9702         this.bodyEl.dom.scrollLeft = x;
9703
9704     },
9705      /**
9706      * Focuses the specified cell.
9707      * @param {Number} row The row index
9708      * @param {Number} col The column index
9709      * @param {Boolean} hscroll false to disable horizontal scrolling
9710      */
9711     focusCell : function(row, col, hscroll)
9712     {
9713         //Roo.log('GridView.focusCell');
9714         var el = this.ensureVisible(row, col, hscroll);
9715         // not sure what focusEL achives = it's a <a> pos relative 
9716         //this.focusEl.alignTo(el, "tl-tl");
9717         //if(Roo.isGecko){
9718         //    this.focusEl.focus();
9719         //}else{
9720         //    this.focusEl.focus.defer(1, this.focusEl);
9721         //}
9722     },
9723     
9724      /**
9725      * Scrolls the specified cell into view
9726      * @param {Number} row The row index
9727      * @param {Number} col The column index
9728      * @param {Boolean} hscroll false to disable horizontal scrolling
9729      */
9730     ensureVisible : function(row, col, hscroll)
9731     {
9732         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9733         //return null; //disable for testing.
9734         if(typeof row != "number"){
9735             row = row.rowIndex;
9736         }
9737         if(row < 0 && row >= this.ds.getCount()){
9738             return  null;
9739         }
9740         col = (col !== undefined ? col : 0);
9741         var cm = this.cm;
9742         while(cm.isHidden(col)){
9743             col++;
9744         }
9745
9746         var el = this.getCellDom(row, col);
9747         if(!el){
9748             return null;
9749         }
9750         var c = this.bodyEl.dom;
9751
9752         var ctop = parseInt(el.offsetTop, 10);
9753         var cleft = parseInt(el.offsetLeft, 10);
9754         var cbot = ctop + el.offsetHeight;
9755         var cright = cleft + el.offsetWidth;
9756
9757         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9758         var ch = 0; //?? header is not withing the area?
9759         var stop = parseInt(c.scrollTop, 10);
9760         var sleft = parseInt(c.scrollLeft, 10);
9761         var sbot = stop + ch;
9762         var sright = sleft + c.clientWidth;
9763         /*
9764         Roo.log('GridView.ensureVisible:' +
9765                 ' ctop:' + ctop +
9766                 ' c.clientHeight:' + c.clientHeight +
9767                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9768                 ' stop:' + stop +
9769                 ' cbot:' + cbot +
9770                 ' sbot:' + sbot +
9771                 ' ch:' + ch  
9772                 );
9773         */
9774         if(ctop < stop){
9775             c.scrollTop = ctop;
9776             //Roo.log("set scrolltop to ctop DISABLE?");
9777         }else if(cbot > sbot){
9778             //Roo.log("set scrolltop to cbot-ch");
9779             c.scrollTop = cbot-ch;
9780         }
9781
9782         if(hscroll !== false){
9783             if(cleft < sleft){
9784                 c.scrollLeft = cleft;
9785             }else if(cright > sright){
9786                 c.scrollLeft = cright-c.clientWidth;
9787             }
9788         }
9789
9790         return el;
9791     },
9792     
9793     
9794     insertRow : function(dm, rowIndex, isUpdate){
9795         
9796         if(!isUpdate){
9797             this.fireEvent("beforerowsinserted", this, rowIndex);
9798         }
9799             //var s = this.getScrollState();
9800         var row = this.renderRow(this.cm, this.store, rowIndex);
9801         // insert before rowIndex..
9802         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9803         
9804         var _this = this;
9805                 
9806         if(row.cellObjects.length){
9807             Roo.each(row.cellObjects, function(r){
9808                 _this.renderCellObject(r);
9809             })
9810         }
9811             
9812         if(!isUpdate){
9813             this.fireEvent("rowsinserted", this, rowIndex);
9814             //this.syncRowHeights(firstRow, lastRow);
9815             //this.stripeRows(firstRow);
9816             //this.layout();
9817         }
9818         
9819     },
9820     
9821     
9822     getRowDom : function(rowIndex)
9823     {
9824         var rows = this.el.select('tbody > tr', true).elements;
9825         
9826         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9827         
9828     },
9829     getCellDom : function(rowIndex, colIndex)
9830     {
9831         var row = this.getRowDom(rowIndex);
9832         if (row === false) {
9833             return false;
9834         }
9835         var cols = row.select('td', true).elements;
9836         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9837         
9838     },
9839     
9840     // returns the object tree for a tr..
9841   
9842     
9843     renderRow : function(cm, ds, rowIndex) 
9844     {
9845         var d = ds.getAt(rowIndex);
9846         
9847         var row = {
9848             tag : 'tr',
9849             cls : 'x-row-' + rowIndex,
9850             cn : []
9851         };
9852             
9853         var cellObjects = [];
9854         
9855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9856             var config = cm.config[i];
9857             
9858             var renderer = cm.getRenderer(i);
9859             var value = '';
9860             var id = false;
9861             
9862             if(typeof(renderer) !== 'undefined'){
9863                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9864             }
9865             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9866             // and are rendered into the cells after the row is rendered - using the id for the element.
9867             
9868             if(typeof(value) === 'object'){
9869                 id = Roo.id();
9870                 cellObjects.push({
9871                     container : id,
9872                     cfg : value 
9873                 })
9874             }
9875             
9876             var rowcfg = {
9877                 record: d,
9878                 rowIndex : rowIndex,
9879                 colIndex : i,
9880                 rowClass : ''
9881             };
9882
9883             this.fireEvent('rowclass', this, rowcfg);
9884             
9885             var td = {
9886                 tag: 'td',
9887                 // this might end up displaying HTML?
9888                 // this is too messy... - better to only do it on columsn you know are going to be too long
9889                 //tooltip : (typeof(value) === 'object') ? '' : value,
9890                 cls : rowcfg.rowClass + ' x-col-' + i,
9891                 style: '',
9892                 html: (typeof(value) === 'object') ? '' : value
9893             };
9894             
9895             if (id) {
9896                 td.id = id;
9897             }
9898             
9899             if(typeof(config.colspan) != 'undefined'){
9900                 td.colspan = config.colspan;
9901             }
9902             
9903             
9904             
9905             if(typeof(config.align) != 'undefined' && config.align.length){
9906                 td.style += ' text-align:' + config.align + ';';
9907             }
9908             if(typeof(config.valign) != 'undefined' && config.valign.length){
9909                 td.style += ' vertical-align:' + config.valign + ';';
9910             }
9911             /*
9912             if(typeof(config.width) != 'undefined'){
9913                 td.style += ' width:' +  config.width + 'px;';
9914             }
9915             */
9916             
9917             if(typeof(config.cursor) != 'undefined'){
9918                 td.style += ' cursor:' +  config.cursor + ';';
9919             }
9920             
9921             if(typeof(config.cls) != 'undefined'){
9922                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9923             }
9924             if (this.responsive) {
9925                 ['xs','sm','md','lg'].map(function(size){
9926                     
9927                     if(typeof(config[size]) == 'undefined'){
9928                         return;
9929                     }
9930                     
9931                     
9932                       
9933                     if (!config[size]) { // 0 = hidden
9934                         // BS 4 '0' is treated as hide that column and below.
9935                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9936                         return;
9937                     }
9938                     
9939                     td.cls += ' col-' + size + '-' + config[size] + (
9940                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9941                     );
9942                      
9943     
9944                 });
9945             }
9946             row.cn.push(td);
9947            
9948         }
9949         
9950         row.cellObjects = cellObjects;
9951         
9952         return row;
9953           
9954     },
9955     
9956     
9957     
9958     onBeforeLoad : function()
9959     {
9960         
9961     },
9962      /**
9963      * Remove all rows
9964      */
9965     clear : function()
9966     {
9967         this.el.select('tbody', true).first().dom.innerHTML = '';
9968     },
9969     /**
9970      * Show or hide a row.
9971      * @param {Number} rowIndex to show or hide
9972      * @param {Boolean} state hide
9973      */
9974     setRowVisibility : function(rowIndex, state)
9975     {
9976         var bt = this.bodyEl.dom;
9977         
9978         var rows = this.el.select('tbody > tr', true).elements;
9979         
9980         if(typeof(rows[rowIndex]) == 'undefined'){
9981             return;
9982         }
9983         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9984         
9985     },
9986     
9987     
9988     getSelectionModel : function(){
9989         if(!this.selModel){
9990             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9991         }
9992         return this.selModel;
9993     },
9994     /*
9995      * Render the Roo.bootstrap object from renderder
9996      */
9997     renderCellObject : function(r)
9998     {
9999         var _this = this;
10000         
10001         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10002         
10003         var t = r.cfg.render(r.container);
10004         
10005         if(r.cfg.cn){
10006             Roo.each(r.cfg.cn, function(c){
10007                 var child = {
10008                     container: t.getChildContainer(),
10009                     cfg: c
10010                 };
10011                 _this.renderCellObject(child);
10012             })
10013         }
10014     },
10015     /**
10016      * get the Row Index from a dom element.
10017      * @param {Roo.Element} row The row to look for
10018      * @returns {Number} the row
10019      */
10020     getRowIndex : function(row)
10021     {
10022         var rowIndex = -1;
10023         
10024         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10025             if(el != row){
10026                 return;
10027             }
10028             
10029             rowIndex = index;
10030         });
10031         
10032         return rowIndex;
10033     },
10034     /**
10035      * get the header TH element for columnIndex
10036      * @param {Number} columnIndex
10037      * @returns {Roo.Element}
10038      */
10039     getHeaderIndex: function(colIndex)
10040     {
10041         var cols = this.headEl.select('th', true).elements;
10042         return cols[colIndex]; 
10043     },
10044     /**
10045      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10046      * @param {domElement} cell to look for
10047      * @returns {Number} the column
10048      */
10049     getCellIndex : function(cell)
10050     {
10051         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10052         if(id){
10053             return parseInt(id[1], 10);
10054         }
10055         return 0;
10056     },
10057      /**
10058      * Returns the grid's underlying element = used by panel.Grid
10059      * @return {Element} The element
10060      */
10061     getGridEl : function(){
10062         return this.el;
10063     },
10064      /**
10065      * Forces a resize - used by panel.Grid
10066      * @return {Element} The element
10067      */
10068     autoSize : function()
10069     {
10070         //var ctr = Roo.get(this.container.dom.parentElement);
10071         var ctr = Roo.get(this.el.dom);
10072         
10073         var thd = this.getGridEl().select('thead',true).first();
10074         var tbd = this.getGridEl().select('tbody', true).first();
10075         var tfd = this.getGridEl().select('tfoot', true).first();
10076         
10077         var cw = ctr.getWidth();
10078         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10079         
10080         if (tbd) {
10081             
10082             tbd.setWidth(ctr.getWidth());
10083             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10084             // this needs fixing for various usage - currently only hydra job advers I think..
10085             //tdb.setHeight(
10086             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10087             //); 
10088             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10089             cw -= barsize;
10090         }
10091         cw = Math.max(cw, this.totalWidth);
10092         this.getGridEl().select('tbody tr',true).setWidth(cw);
10093         this.initCSS();
10094         
10095         // resize 'expandable coloumn?
10096         
10097         return; // we doe not have a view in this design..
10098         
10099     },
10100     onBodyScroll: function()
10101     {
10102         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10103         if(this.headEl){
10104             this.headEl.setStyle({
10105                 'position' : 'relative',
10106                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10107             });
10108         }
10109         
10110         if(this.lazyLoad){
10111             
10112             var scrollHeight = this.bodyEl.dom.scrollHeight;
10113             
10114             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10115             
10116             var height = this.bodyEl.getHeight();
10117             
10118             if(scrollHeight - height == scrollTop) {
10119                 
10120                 var total = this.ds.getTotalCount();
10121                 
10122                 if(this.footer.cursor + this.footer.pageSize < total){
10123                     
10124                     this.footer.ds.load({
10125                         params : {
10126                             start : this.footer.cursor + this.footer.pageSize,
10127                             limit : this.footer.pageSize
10128                         },
10129                         add : true
10130                     });
10131                 }
10132             }
10133             
10134         }
10135     },
10136     onColumnSplitterMoved : function(i, diff)
10137     {
10138         this.userResized = true;
10139         
10140         var cm = this.colModel;
10141         
10142         var w = this.getHeaderIndex(i).getWidth() + diff;
10143         
10144         
10145         cm.setColumnWidth(i, w, true);
10146         this.initCSS();
10147         //var cid = cm.getColumnId(i); << not used in this version?
10148        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10149         
10150         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10151         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10152         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10153 */
10154         //this.updateSplitters();
10155         //this.layout(); << ??
10156         this.fireEvent("columnresize", i, w);
10157     },
10158     onHeaderChange : function()
10159     {
10160         var header = this.renderHeader();
10161         var table = this.el.select('table', true).first();
10162         
10163         this.headEl.remove();
10164         this.headEl = table.createChild(header, this.bodyEl, false);
10165         
10166         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10167             e.on('click', this.sort, this);
10168         }, this);
10169         
10170         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10171             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10172         }
10173         
10174     },
10175     
10176     onHiddenChange : function(colModel, colIndex, hidden)
10177     {
10178         /*
10179         this.cm.setHidden()
10180         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10181         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10182         
10183         this.CSS.updateRule(thSelector, "display", "");
10184         this.CSS.updateRule(tdSelector, "display", "");
10185         
10186         if(hidden){
10187             this.CSS.updateRule(thSelector, "display", "none");
10188             this.CSS.updateRule(tdSelector, "display", "none");
10189         }
10190         */
10191         // onload calls initCSS()
10192         this.onHeaderChange();
10193         this.onLoad();
10194     },
10195     
10196     setColumnWidth: function(col_index, width)
10197     {
10198         // width = "md-2 xs-2..."
10199         if(!this.colModel.config[col_index]) {
10200             return;
10201         }
10202         
10203         var w = width.split(" ");
10204         
10205         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10206         
10207         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10208         
10209         
10210         for(var j = 0; j < w.length; j++) {
10211             
10212             if(!w[j]) {
10213                 continue;
10214             }
10215             
10216             var size_cls = w[j].split("-");
10217             
10218             if(!Number.isInteger(size_cls[1] * 1)) {
10219                 continue;
10220             }
10221             
10222             if(!this.colModel.config[col_index][size_cls[0]]) {
10223                 continue;
10224             }
10225             
10226             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10227                 continue;
10228             }
10229             
10230             h_row[0].classList.replace(
10231                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10232                 "col-"+size_cls[0]+"-"+size_cls[1]
10233             );
10234             
10235             for(var i = 0; i < rows.length; i++) {
10236                 
10237                 var size_cls = w[j].split("-");
10238                 
10239                 if(!Number.isInteger(size_cls[1] * 1)) {
10240                     continue;
10241                 }
10242                 
10243                 if(!this.colModel.config[col_index][size_cls[0]]) {
10244                     continue;
10245                 }
10246                 
10247                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10248                     continue;
10249                 }
10250                 
10251                 rows[i].classList.replace(
10252                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10253                     "col-"+size_cls[0]+"-"+size_cls[1]
10254                 );
10255             }
10256             
10257             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10258         }
10259     }
10260 });
10261
10262 // currently only used to find the split on drag.. 
10263 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10264
10265 /**
10266  * @depricated
10267 */
10268 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10269 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10270 /*
10271  * - LGPL
10272  *
10273  * table cell
10274  * 
10275  */
10276
10277 /**
10278  * @class Roo.bootstrap.TableCell
10279  * @extends Roo.bootstrap.Component
10280  * Bootstrap TableCell class
10281  * @cfg {String} html cell contain text
10282  * @cfg {String} cls cell class
10283  * @cfg {String} tag cell tag (td|th) default td
10284  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10285  * @cfg {String} align Aligns the content in a cell
10286  * @cfg {String} axis Categorizes cells
10287  * @cfg {String} bgcolor Specifies the background color of a cell
10288  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10289  * @cfg {Number} colspan Specifies the number of columns a cell should span
10290  * @cfg {String} headers Specifies one or more header cells a cell is related to
10291  * @cfg {Number} height Sets the height of a cell
10292  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10293  * @cfg {Number} rowspan Sets the number of rows a cell should span
10294  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10295  * @cfg {String} valign Vertical aligns the content in a cell
10296  * @cfg {Number} width Specifies the width of a cell
10297  * 
10298  * @constructor
10299  * Create a new TableCell
10300  * @param {Object} config The config object
10301  */
10302
10303 Roo.bootstrap.TableCell = function(config){
10304     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10305 };
10306
10307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10308     
10309     html: false,
10310     cls: false,
10311     tag: false,
10312     abbr: false,
10313     align: false,
10314     axis: false,
10315     bgcolor: false,
10316     charoff: false,
10317     colspan: false,
10318     headers: false,
10319     height: false,
10320     nowrap: false,
10321     rowspan: false,
10322     scope: false,
10323     valign: false,
10324     width: false,
10325     
10326     
10327     getAutoCreate : function(){
10328         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10329         
10330         cfg = {
10331             tag: 'td'
10332         };
10333         
10334         if(this.tag){
10335             cfg.tag = this.tag;
10336         }
10337         
10338         if (this.html) {
10339             cfg.html=this.html
10340         }
10341         if (this.cls) {
10342             cfg.cls=this.cls
10343         }
10344         if (this.abbr) {
10345             cfg.abbr=this.abbr
10346         }
10347         if (this.align) {
10348             cfg.align=this.align
10349         }
10350         if (this.axis) {
10351             cfg.axis=this.axis
10352         }
10353         if (this.bgcolor) {
10354             cfg.bgcolor=this.bgcolor
10355         }
10356         if (this.charoff) {
10357             cfg.charoff=this.charoff
10358         }
10359         if (this.colspan) {
10360             cfg.colspan=this.colspan
10361         }
10362         if (this.headers) {
10363             cfg.headers=this.headers
10364         }
10365         if (this.height) {
10366             cfg.height=this.height
10367         }
10368         if (this.nowrap) {
10369             cfg.nowrap=this.nowrap
10370         }
10371         if (this.rowspan) {
10372             cfg.rowspan=this.rowspan
10373         }
10374         if (this.scope) {
10375             cfg.scope=this.scope
10376         }
10377         if (this.valign) {
10378             cfg.valign=this.valign
10379         }
10380         if (this.width) {
10381             cfg.width=this.width
10382         }
10383         
10384         
10385         return cfg;
10386     }
10387    
10388 });
10389
10390  
10391
10392  /*
10393  * - LGPL
10394  *
10395  * table row
10396  * 
10397  */
10398
10399 /**
10400  * @class Roo.bootstrap.TableRow
10401  * @extends Roo.bootstrap.Component
10402  * Bootstrap TableRow class
10403  * @cfg {String} cls row class
10404  * @cfg {String} align Aligns the content in a table row
10405  * @cfg {String} bgcolor Specifies a background color for a table row
10406  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10407  * @cfg {String} valign Vertical aligns the content in a table row
10408  * 
10409  * @constructor
10410  * Create a new TableRow
10411  * @param {Object} config The config object
10412  */
10413
10414 Roo.bootstrap.TableRow = function(config){
10415     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10416 };
10417
10418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10419     
10420     cls: false,
10421     align: false,
10422     bgcolor: false,
10423     charoff: false,
10424     valign: false,
10425     
10426     getAutoCreate : function(){
10427         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10428         
10429         cfg = {
10430             tag: 'tr'
10431         };
10432             
10433         if(this.cls){
10434             cfg.cls = this.cls;
10435         }
10436         if(this.align){
10437             cfg.align = this.align;
10438         }
10439         if(this.bgcolor){
10440             cfg.bgcolor = this.bgcolor;
10441         }
10442         if(this.charoff){
10443             cfg.charoff = this.charoff;
10444         }
10445         if(this.valign){
10446             cfg.valign = this.valign;
10447         }
10448         
10449         return cfg;
10450     }
10451    
10452 });
10453
10454  
10455
10456  /*
10457  * - LGPL
10458  *
10459  * table body
10460  * 
10461  */
10462
10463 /**
10464  * @class Roo.bootstrap.TableBody
10465  * @extends Roo.bootstrap.Component
10466  * Bootstrap TableBody class
10467  * @cfg {String} cls element class
10468  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10469  * @cfg {String} align Aligns the content inside the element
10470  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10471  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10472  * 
10473  * @constructor
10474  * Create a new TableBody
10475  * @param {Object} config The config object
10476  */
10477
10478 Roo.bootstrap.TableBody = function(config){
10479     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10480 };
10481
10482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10483     
10484     cls: false,
10485     tag: false,
10486     align: false,
10487     charoff: false,
10488     valign: false,
10489     
10490     getAutoCreate : function(){
10491         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10492         
10493         cfg = {
10494             tag: 'tbody'
10495         };
10496             
10497         if (this.cls) {
10498             cfg.cls=this.cls
10499         }
10500         if(this.tag){
10501             cfg.tag = this.tag;
10502         }
10503         
10504         if(this.align){
10505             cfg.align = this.align;
10506         }
10507         if(this.charoff){
10508             cfg.charoff = this.charoff;
10509         }
10510         if(this.valign){
10511             cfg.valign = this.valign;
10512         }
10513         
10514         return cfg;
10515     }
10516     
10517     
10518 //    initEvents : function()
10519 //    {
10520 //        
10521 //        if(!this.store){
10522 //            return;
10523 //        }
10524 //        
10525 //        this.store = Roo.factory(this.store, Roo.data);
10526 //        this.store.on('load', this.onLoad, this);
10527 //        
10528 //        this.store.load();
10529 //        
10530 //    },
10531 //    
10532 //    onLoad: function () 
10533 //    {   
10534 //        this.fireEvent('load', this);
10535 //    }
10536 //    
10537 //   
10538 });
10539
10540  
10541
10542  /*
10543  * Based on:
10544  * Ext JS Library 1.1.1
10545  * Copyright(c) 2006-2007, Ext JS, LLC.
10546  *
10547  * Originally Released Under LGPL - original licence link has changed is not relivant.
10548  *
10549  * Fork - LGPL
10550  * <script type="text/javascript">
10551  */
10552
10553 // as we use this in bootstrap.
10554 Roo.namespace('Roo.form');
10555  /**
10556  * @class Roo.form.Action
10557  * Internal Class used to handle form actions
10558  * @constructor
10559  * @param {Roo.form.BasicForm} el The form element or its id
10560  * @param {Object} config Configuration options
10561  */
10562
10563  
10564  
10565 // define the action interface
10566 Roo.form.Action = function(form, options){
10567     this.form = form;
10568     this.options = options || {};
10569 };
10570 /**
10571  * Client Validation Failed
10572  * @const 
10573  */
10574 Roo.form.Action.CLIENT_INVALID = 'client';
10575 /**
10576  * Server Validation Failed
10577  * @const 
10578  */
10579 Roo.form.Action.SERVER_INVALID = 'server';
10580  /**
10581  * Connect to Server Failed
10582  * @const 
10583  */
10584 Roo.form.Action.CONNECT_FAILURE = 'connect';
10585 /**
10586  * Reading Data from Server Failed
10587  * @const 
10588  */
10589 Roo.form.Action.LOAD_FAILURE = 'load';
10590
10591 Roo.form.Action.prototype = {
10592     type : 'default',
10593     failureType : undefined,
10594     response : undefined,
10595     result : undefined,
10596
10597     // interface method
10598     run : function(options){
10599
10600     },
10601
10602     // interface method
10603     success : function(response){
10604
10605     },
10606
10607     // interface method
10608     handleResponse : function(response){
10609
10610     },
10611
10612     // default connection failure
10613     failure : function(response){
10614         
10615         this.response = response;
10616         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10617         this.form.afterAction(this, false);
10618     },
10619
10620     processResponse : function(response){
10621         this.response = response;
10622         if(!response.responseText){
10623             return true;
10624         }
10625         this.result = this.handleResponse(response);
10626         return this.result;
10627     },
10628
10629     // utility functions used internally
10630     getUrl : function(appendParams){
10631         var url = this.options.url || this.form.url || this.form.el.dom.action;
10632         if(appendParams){
10633             var p = this.getParams();
10634             if(p){
10635                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10636             }
10637         }
10638         return url;
10639     },
10640
10641     getMethod : function(){
10642         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10643     },
10644
10645     getParams : function(){
10646         var bp = this.form.baseParams;
10647         var p = this.options.params;
10648         if(p){
10649             if(typeof p == "object"){
10650                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10651             }else if(typeof p == 'string' && bp){
10652                 p += '&' + Roo.urlEncode(bp);
10653             }
10654         }else if(bp){
10655             p = Roo.urlEncode(bp);
10656         }
10657         return p;
10658     },
10659
10660     createCallback : function(){
10661         return {
10662             success: this.success,
10663             failure: this.failure,
10664             scope: this,
10665             timeout: (this.form.timeout*1000),
10666             upload: this.form.fileUpload ? this.success : undefined
10667         };
10668     }
10669 };
10670
10671 Roo.form.Action.Submit = function(form, options){
10672     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10673 };
10674
10675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10676     type : 'submit',
10677
10678     haveProgress : false,
10679     uploadComplete : false,
10680     
10681     // uploadProgress indicator.
10682     uploadProgress : function()
10683     {
10684         if (!this.form.progressUrl) {
10685             return;
10686         }
10687         
10688         if (!this.haveProgress) {
10689             Roo.MessageBox.progress("Uploading", "Uploading");
10690         }
10691         if (this.uploadComplete) {
10692            Roo.MessageBox.hide();
10693            return;
10694         }
10695         
10696         this.haveProgress = true;
10697    
10698         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10699         
10700         var c = new Roo.data.Connection();
10701         c.request({
10702             url : this.form.progressUrl,
10703             params: {
10704                 id : uid
10705             },
10706             method: 'GET',
10707             success : function(req){
10708                //console.log(data);
10709                 var rdata = false;
10710                 var edata;
10711                 try  {
10712                    rdata = Roo.decode(req.responseText)
10713                 } catch (e) {
10714                     Roo.log("Invalid data from server..");
10715                     Roo.log(edata);
10716                     return;
10717                 }
10718                 if (!rdata || !rdata.success) {
10719                     Roo.log(rdata);
10720                     Roo.MessageBox.alert(Roo.encode(rdata));
10721                     return;
10722                 }
10723                 var data = rdata.data;
10724                 
10725                 if (this.uploadComplete) {
10726                    Roo.MessageBox.hide();
10727                    return;
10728                 }
10729                    
10730                 if (data){
10731                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10732                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10733                     );
10734                 }
10735                 this.uploadProgress.defer(2000,this);
10736             },
10737        
10738             failure: function(data) {
10739                 Roo.log('progress url failed ');
10740                 Roo.log(data);
10741             },
10742             scope : this
10743         });
10744            
10745     },
10746     
10747     
10748     run : function()
10749     {
10750         // run get Values on the form, so it syncs any secondary forms.
10751         this.form.getValues();
10752         
10753         var o = this.options;
10754         var method = this.getMethod();
10755         var isPost = method == 'POST';
10756         if(o.clientValidation === false || this.form.isValid()){
10757             
10758             if (this.form.progressUrl) {
10759                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10760                     (new Date() * 1) + '' + Math.random());
10761                     
10762             } 
10763             
10764             
10765             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10766                 form:this.form.el.dom,
10767                 url:this.getUrl(!isPost),
10768                 method: method,
10769                 params:isPost ? this.getParams() : null,
10770                 isUpload: this.form.fileUpload,
10771                 formData : this.form.formData
10772             }));
10773             
10774             this.uploadProgress();
10775
10776         }else if (o.clientValidation !== false){ // client validation failed
10777             this.failureType = Roo.form.Action.CLIENT_INVALID;
10778             this.form.afterAction(this, false);
10779         }
10780     },
10781
10782     success : function(response)
10783     {
10784         this.uploadComplete= true;
10785         if (this.haveProgress) {
10786             Roo.MessageBox.hide();
10787         }
10788         
10789         
10790         var result = this.processResponse(response);
10791         if(result === true || result.success){
10792             this.form.afterAction(this, true);
10793             return;
10794         }
10795         if(result.errors){
10796             this.form.markInvalid(result.errors);
10797             this.failureType = Roo.form.Action.SERVER_INVALID;
10798         }
10799         this.form.afterAction(this, false);
10800     },
10801     failure : function(response)
10802     {
10803         this.uploadComplete= true;
10804         if (this.haveProgress) {
10805             Roo.MessageBox.hide();
10806         }
10807         
10808         this.response = response;
10809         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10810         this.form.afterAction(this, false);
10811     },
10812     
10813     handleResponse : function(response){
10814         if(this.form.errorReader){
10815             var rs = this.form.errorReader.read(response);
10816             var errors = [];
10817             if(rs.records){
10818                 for(var i = 0, len = rs.records.length; i < len; i++) {
10819                     var r = rs.records[i];
10820                     errors[i] = r.data;
10821                 }
10822             }
10823             if(errors.length < 1){
10824                 errors = null;
10825             }
10826             return {
10827                 success : rs.success,
10828                 errors : errors
10829             };
10830         }
10831         var ret = false;
10832         try {
10833             ret = Roo.decode(response.responseText);
10834         } catch (e) {
10835             ret = {
10836                 success: false,
10837                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10838                 errors : []
10839             };
10840         }
10841         return ret;
10842         
10843     }
10844 });
10845
10846
10847 Roo.form.Action.Load = function(form, options){
10848     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10849     this.reader = this.form.reader;
10850 };
10851
10852 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10853     type : 'load',
10854
10855     run : function(){
10856         
10857         Roo.Ajax.request(Roo.apply(
10858                 this.createCallback(), {
10859                     method:this.getMethod(),
10860                     url:this.getUrl(false),
10861                     params:this.getParams()
10862         }));
10863     },
10864
10865     success : function(response){
10866         
10867         var result = this.processResponse(response);
10868         if(result === true || !result.success || !result.data){
10869             this.failureType = Roo.form.Action.LOAD_FAILURE;
10870             this.form.afterAction(this, false);
10871             return;
10872         }
10873         this.form.clearInvalid();
10874         this.form.setValues(result.data);
10875         this.form.afterAction(this, true);
10876     },
10877
10878     handleResponse : function(response){
10879         if(this.form.reader){
10880             var rs = this.form.reader.read(response);
10881             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10882             return {
10883                 success : rs.success,
10884                 data : data
10885             };
10886         }
10887         return Roo.decode(response.responseText);
10888     }
10889 });
10890
10891 Roo.form.Action.ACTION_TYPES = {
10892     'load' : Roo.form.Action.Load,
10893     'submit' : Roo.form.Action.Submit
10894 };/*
10895  * - LGPL
10896  *
10897  * form
10898  *
10899  */
10900
10901 /**
10902  * @class Roo.bootstrap.Form
10903  * @extends Roo.bootstrap.Component
10904  * Bootstrap Form class
10905  * @cfg {String} method  GET | POST (default POST)
10906  * @cfg {String} labelAlign top | left (default top)
10907  * @cfg {String} align left  | right - for navbars
10908  * @cfg {Boolean} loadMask load mask when submit (default true)
10909
10910  *
10911  * @constructor
10912  * Create a new Form
10913  * @param {Object} config The config object
10914  */
10915
10916
10917 Roo.bootstrap.Form = function(config){
10918     
10919     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10920     
10921     Roo.bootstrap.Form.popover.apply();
10922     
10923     this.addEvents({
10924         /**
10925          * @event clientvalidation
10926          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10927          * @param {Form} this
10928          * @param {Boolean} valid true if the form has passed client-side validation
10929          */
10930         clientvalidation: true,
10931         /**
10932          * @event beforeaction
10933          * Fires before any action is performed. Return false to cancel the action.
10934          * @param {Form} this
10935          * @param {Action} action The action to be performed
10936          */
10937         beforeaction: true,
10938         /**
10939          * @event actionfailed
10940          * Fires when an action fails.
10941          * @param {Form} this
10942          * @param {Action} action The action that failed
10943          */
10944         actionfailed : true,
10945         /**
10946          * @event actioncomplete
10947          * Fires when an action is completed.
10948          * @param {Form} this
10949          * @param {Action} action The action that completed
10950          */
10951         actioncomplete : true
10952     });
10953 };
10954
10955 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10956
10957      /**
10958      * @cfg {String} method
10959      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10960      */
10961     method : 'POST',
10962     /**
10963      * @cfg {String} url
10964      * The URL to use for form actions if one isn't supplied in the action options.
10965      */
10966     /**
10967      * @cfg {Boolean} fileUpload
10968      * Set to true if this form is a file upload.
10969      */
10970
10971     /**
10972      * @cfg {Object} baseParams
10973      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10974      */
10975
10976     /**
10977      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10978      */
10979     timeout: 30,
10980     /**
10981      * @cfg {Sting} align (left|right) for navbar forms
10982      */
10983     align : 'left',
10984
10985     // private
10986     activeAction : null,
10987
10988     /**
10989      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10990      * element by passing it or its id or mask the form itself by passing in true.
10991      * @type Mixed
10992      */
10993     waitMsgTarget : false,
10994
10995     loadMask : true,
10996     
10997     /**
10998      * @cfg {Boolean} errorMask (true|false) default false
10999      */
11000     errorMask : false,
11001     
11002     /**
11003      * @cfg {Number} maskOffset Default 100
11004      */
11005     maskOffset : 100,
11006     
11007     /**
11008      * @cfg {Boolean} maskBody
11009      */
11010     maskBody : false,
11011
11012     getAutoCreate : function(){
11013
11014         var cfg = {
11015             tag: 'form',
11016             method : this.method || 'POST',
11017             id : this.id || Roo.id(),
11018             cls : ''
11019         };
11020         if (this.parent().xtype.match(/^Nav/)) {
11021             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11022
11023         }
11024
11025         if (this.labelAlign == 'left' ) {
11026             cfg.cls += ' form-horizontal';
11027         }
11028
11029
11030         return cfg;
11031     },
11032     initEvents : function()
11033     {
11034         this.el.on('submit', this.onSubmit, this);
11035         // this was added as random key presses on the form where triggering form submit.
11036         this.el.on('keypress', function(e) {
11037             if (e.getCharCode() != 13) {
11038                 return true;
11039             }
11040             // we might need to allow it for textareas.. and some other items.
11041             // check e.getTarget().
11042
11043             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11044                 return true;
11045             }
11046
11047             Roo.log("keypress blocked");
11048
11049             e.preventDefault();
11050             return false;
11051         });
11052         
11053     },
11054     // private
11055     onSubmit : function(e){
11056         e.stopEvent();
11057     },
11058
11059      /**
11060      * Returns true if client-side validation on the form is successful.
11061      * @return Boolean
11062      */
11063     isValid : function(){
11064         var items = this.getItems();
11065         var valid = true;
11066         var target = false;
11067         
11068         items.each(function(f){
11069             
11070             if(f.validate()){
11071                 return;
11072             }
11073             
11074             Roo.log('invalid field: ' + f.name);
11075             
11076             valid = false;
11077
11078             if(!target && f.el.isVisible(true)){
11079                 target = f;
11080             }
11081            
11082         });
11083         
11084         if(this.errorMask && !valid){
11085             Roo.bootstrap.Form.popover.mask(this, target);
11086         }
11087         
11088         return valid;
11089     },
11090     
11091     /**
11092      * Returns true if any fields in this form have changed since their original load.
11093      * @return Boolean
11094      */
11095     isDirty : function(){
11096         var dirty = false;
11097         var items = this.getItems();
11098         items.each(function(f){
11099            if(f.isDirty()){
11100                dirty = true;
11101                return false;
11102            }
11103            return true;
11104         });
11105         return dirty;
11106     },
11107      /**
11108      * Performs a predefined action (submit or load) or custom actions you define on this form.
11109      * @param {String} actionName The name of the action type
11110      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11111      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11112      * accept other config options):
11113      * <pre>
11114 Property          Type             Description
11115 ----------------  ---------------  ----------------------------------------------------------------------------------
11116 url               String           The url for the action (defaults to the form's url)
11117 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11118 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11119 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11120                                    validate the form on the client (defaults to false)
11121      * </pre>
11122      * @return {BasicForm} this
11123      */
11124     doAction : function(action, options){
11125         if(typeof action == 'string'){
11126             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11127         }
11128         if(this.fireEvent('beforeaction', this, action) !== false){
11129             this.beforeAction(action);
11130             action.run.defer(100, action);
11131         }
11132         return this;
11133     },
11134
11135     // private
11136     beforeAction : function(action){
11137         var o = action.options;
11138         
11139         if(this.loadMask){
11140             
11141             if(this.maskBody){
11142                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11143             } else {
11144                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11145             }
11146         }
11147         // not really supported yet.. ??
11148
11149         //if(this.waitMsgTarget === true){
11150         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151         //}else if(this.waitMsgTarget){
11152         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11153         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154         //}else {
11155         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11156        // }
11157
11158     },
11159
11160     // private
11161     afterAction : function(action, success){
11162         this.activeAction = null;
11163         var o = action.options;
11164
11165         if(this.loadMask){
11166             
11167             if(this.maskBody){
11168                 Roo.get(document.body).unmask();
11169             } else {
11170                 this.el.unmask();
11171             }
11172         }
11173         
11174         //if(this.waitMsgTarget === true){
11175 //            this.el.unmask();
11176         //}else if(this.waitMsgTarget){
11177         //    this.waitMsgTarget.unmask();
11178         //}else{
11179         //    Roo.MessageBox.updateProgress(1);
11180         //    Roo.MessageBox.hide();
11181        // }
11182         //
11183         if(success){
11184             if(o.reset){
11185                 this.reset();
11186             }
11187             Roo.callback(o.success, o.scope, [this, action]);
11188             this.fireEvent('actioncomplete', this, action);
11189
11190         }else{
11191
11192             // failure condition..
11193             // we have a scenario where updates need confirming.
11194             // eg. if a locking scenario exists..
11195             // we look for { errors : { needs_confirm : true }} in the response.
11196             if (
11197                 (typeof(action.result) != 'undefined')  &&
11198                 (typeof(action.result.errors) != 'undefined')  &&
11199                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11200            ){
11201                 var _t = this;
11202                 Roo.log("not supported yet");
11203                  /*
11204
11205                 Roo.MessageBox.confirm(
11206                     "Change requires confirmation",
11207                     action.result.errorMsg,
11208                     function(r) {
11209                         if (r != 'yes') {
11210                             return;
11211                         }
11212                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11213                     }
11214
11215                 );
11216                 */
11217
11218
11219                 return;
11220             }
11221
11222             Roo.callback(o.failure, o.scope, [this, action]);
11223             // show an error message if no failed handler is set..
11224             if (!this.hasListener('actionfailed')) {
11225                 Roo.log("need to add dialog support");
11226                 /*
11227                 Roo.MessageBox.alert("Error",
11228                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11229                         action.result.errorMsg :
11230                         "Saving Failed, please check your entries or try again"
11231                 );
11232                 */
11233             }
11234
11235             this.fireEvent('actionfailed', this, action);
11236         }
11237
11238     },
11239     /**
11240      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11241      * @param {String} id The value to search for
11242      * @return Field
11243      */
11244     findField : function(id){
11245         var items = this.getItems();
11246         var field = items.get(id);
11247         if(!field){
11248              items.each(function(f){
11249                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11250                     field = f;
11251                     return false;
11252                 }
11253                 return true;
11254             });
11255         }
11256         return field || null;
11257     },
11258      /**
11259      * Mark fields in this form invalid in bulk.
11260      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11261      * @return {BasicForm} this
11262      */
11263     markInvalid : function(errors){
11264         if(errors instanceof Array){
11265             for(var i = 0, len = errors.length; i < len; i++){
11266                 var fieldError = errors[i];
11267                 var f = this.findField(fieldError.id);
11268                 if(f){
11269                     f.markInvalid(fieldError.msg);
11270                 }
11271             }
11272         }else{
11273             var field, id;
11274             for(id in errors){
11275                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11276                     field.markInvalid(errors[id]);
11277                 }
11278             }
11279         }
11280         //Roo.each(this.childForms || [], function (f) {
11281         //    f.markInvalid(errors);
11282         //});
11283
11284         return this;
11285     },
11286
11287     /**
11288      * Set values for fields in this form in bulk.
11289      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11290      * @return {BasicForm} this
11291      */
11292     setValues : function(values){
11293         if(values instanceof Array){ // array of objects
11294             for(var i = 0, len = values.length; i < len; i++){
11295                 var v = values[i];
11296                 var f = this.findField(v.id);
11297                 if(f){
11298                     f.setValue(v.value);
11299                     if(this.trackResetOnLoad){
11300                         f.originalValue = f.getValue();
11301                     }
11302                 }
11303             }
11304         }else{ // object hash
11305             var field, id;
11306             for(id in values){
11307                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11308
11309                     if (field.setFromData &&
11310                         field.valueField &&
11311                         field.displayField &&
11312                         // combos' with local stores can
11313                         // be queried via setValue()
11314                         // to set their value..
11315                         (field.store && !field.store.isLocal)
11316                         ) {
11317                         // it's a combo
11318                         var sd = { };
11319                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11320                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11321                         field.setFromData(sd);
11322
11323                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11324                         
11325                         field.setFromData(values);
11326                         
11327                     } else {
11328                         field.setValue(values[id]);
11329                     }
11330
11331
11332                     if(this.trackResetOnLoad){
11333                         field.originalValue = field.getValue();
11334                     }
11335                 }
11336             }
11337         }
11338
11339         //Roo.each(this.childForms || [], function (f) {
11340         //    f.setValues(values);
11341         //});
11342
11343         return this;
11344     },
11345
11346     /**
11347      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11348      * they are returned as an array.
11349      * @param {Boolean} asString
11350      * @return {Object}
11351      */
11352     getValues : function(asString){
11353         //if (this.childForms) {
11354             // copy values from the child forms
11355         //    Roo.each(this.childForms, function (f) {
11356         //        this.setValues(f.getValues());
11357         //    }, this);
11358         //}
11359
11360
11361
11362         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11363         if(asString === true){
11364             return fs;
11365         }
11366         return Roo.urlDecode(fs);
11367     },
11368
11369     /**
11370      * Returns the fields in this form as an object with key/value pairs.
11371      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11372      * @return {Object}
11373      */
11374     getFieldValues : function(with_hidden)
11375     {
11376         var items = this.getItems();
11377         var ret = {};
11378         items.each(function(f){
11379             
11380             if (!f.getName()) {
11381                 return;
11382             }
11383             
11384             var v = f.getValue();
11385             
11386             if (f.inputType =='radio') {
11387                 if (typeof(ret[f.getName()]) == 'undefined') {
11388                     ret[f.getName()] = ''; // empty..
11389                 }
11390
11391                 if (!f.el.dom.checked) {
11392                     return;
11393
11394                 }
11395                 v = f.el.dom.value;
11396
11397             }
11398             
11399             if(f.xtype == 'MoneyField'){
11400                 ret[f.currencyName] = f.getCurrency();
11401             }
11402
11403             // not sure if this supported any more..
11404             if ((typeof(v) == 'object') && f.getRawValue) {
11405                 v = f.getRawValue() ; // dates..
11406             }
11407             // combo boxes where name != hiddenName...
11408             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11409                 ret[f.name] = f.getRawValue();
11410             }
11411             ret[f.getName()] = v;
11412         });
11413
11414         return ret;
11415     },
11416
11417     /**
11418      * Clears all invalid messages in this form.
11419      * @return {BasicForm} this
11420      */
11421     clearInvalid : function(){
11422         var items = this.getItems();
11423
11424         items.each(function(f){
11425            f.clearInvalid();
11426         });
11427
11428         return this;
11429     },
11430
11431     /**
11432      * Resets this form.
11433      * @return {BasicForm} this
11434      */
11435     reset : function(){
11436         var items = this.getItems();
11437         items.each(function(f){
11438             f.reset();
11439         });
11440
11441         Roo.each(this.childForms || [], function (f) {
11442             f.reset();
11443         });
11444
11445
11446         return this;
11447     },
11448     
11449     getItems : function()
11450     {
11451         var r=new Roo.util.MixedCollection(false, function(o){
11452             return o.id || (o.id = Roo.id());
11453         });
11454         var iter = function(el) {
11455             if (el.inputEl) {
11456                 r.add(el);
11457             }
11458             if (!el.items) {
11459                 return;
11460             }
11461             Roo.each(el.items,function(e) {
11462                 iter(e);
11463             });
11464         };
11465
11466         iter(this);
11467         return r;
11468     },
11469     
11470     hideFields : function(items)
11471     {
11472         Roo.each(items, function(i){
11473             
11474             var f = this.findField(i);
11475             
11476             if(!f){
11477                 return;
11478             }
11479             
11480             f.hide();
11481             
11482         }, this);
11483     },
11484     
11485     showFields : function(items)
11486     {
11487         Roo.each(items, function(i){
11488             
11489             var f = this.findField(i);
11490             
11491             if(!f){
11492                 return;
11493             }
11494             
11495             f.show();
11496             
11497         }, this);
11498     }
11499
11500 });
11501
11502 Roo.apply(Roo.bootstrap.Form, {
11503     
11504     popover : {
11505         
11506         padding : 5,
11507         
11508         isApplied : false,
11509         
11510         isMasked : false,
11511         
11512         form : false,
11513         
11514         target : false,
11515         
11516         toolTip : false,
11517         
11518         intervalID : false,
11519         
11520         maskEl : false,
11521         
11522         apply : function()
11523         {
11524             if(this.isApplied){
11525                 return;
11526             }
11527             
11528             this.maskEl = {
11529                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11530                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11531                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11532                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11533             };
11534             
11535             this.maskEl.top.enableDisplayMode("block");
11536             this.maskEl.left.enableDisplayMode("block");
11537             this.maskEl.bottom.enableDisplayMode("block");
11538             this.maskEl.right.enableDisplayMode("block");
11539             
11540             this.toolTip = new Roo.bootstrap.Tooltip({
11541                 cls : 'roo-form-error-popover',
11542                 alignment : {
11543                     'left' : ['r-l', [-2,0], 'right'],
11544                     'right' : ['l-r', [2,0], 'left'],
11545                     'bottom' : ['tl-bl', [0,2], 'top'],
11546                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11547                 }
11548             });
11549             
11550             this.toolTip.render(Roo.get(document.body));
11551
11552             this.toolTip.el.enableDisplayMode("block");
11553             
11554             Roo.get(document.body).on('click', function(){
11555                 this.unmask();
11556             }, this);
11557             
11558             Roo.get(document.body).on('touchstart', function(){
11559                 this.unmask();
11560             }, this);
11561             
11562             this.isApplied = true
11563         },
11564         
11565         mask : function(form, target)
11566         {
11567             this.form = form;
11568             
11569             this.target = target;
11570             
11571             if(!this.form.errorMask || !target.el){
11572                 return;
11573             }
11574             
11575             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11576             
11577             Roo.log(scrollable);
11578             
11579             var ot = this.target.el.calcOffsetsTo(scrollable);
11580             
11581             var scrollTo = ot[1] - this.form.maskOffset;
11582             
11583             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11584             
11585             scrollable.scrollTo('top', scrollTo);
11586             
11587             var box = this.target.el.getBox();
11588             Roo.log(box);
11589             var zIndex = Roo.bootstrap.Modal.zIndex++;
11590
11591             
11592             this.maskEl.top.setStyle('position', 'absolute');
11593             this.maskEl.top.setStyle('z-index', zIndex);
11594             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11595             this.maskEl.top.setLeft(0);
11596             this.maskEl.top.setTop(0);
11597             this.maskEl.top.show();
11598             
11599             this.maskEl.left.setStyle('position', 'absolute');
11600             this.maskEl.left.setStyle('z-index', zIndex);
11601             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11602             this.maskEl.left.setLeft(0);
11603             this.maskEl.left.setTop(box.y - this.padding);
11604             this.maskEl.left.show();
11605
11606             this.maskEl.bottom.setStyle('position', 'absolute');
11607             this.maskEl.bottom.setStyle('z-index', zIndex);
11608             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11609             this.maskEl.bottom.setLeft(0);
11610             this.maskEl.bottom.setTop(box.bottom + this.padding);
11611             this.maskEl.bottom.show();
11612
11613             this.maskEl.right.setStyle('position', 'absolute');
11614             this.maskEl.right.setStyle('z-index', zIndex);
11615             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11616             this.maskEl.right.setLeft(box.right + this.padding);
11617             this.maskEl.right.setTop(box.y - this.padding);
11618             this.maskEl.right.show();
11619
11620             this.toolTip.bindEl = this.target.el;
11621
11622             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11623
11624             var tip = this.target.blankText;
11625
11626             if(this.target.getValue() !== '' ) {
11627                 
11628                 if (this.target.invalidText.length) {
11629                     tip = this.target.invalidText;
11630                 } else if (this.target.regexText.length){
11631                     tip = this.target.regexText;
11632                 }
11633             }
11634
11635             this.toolTip.show(tip);
11636
11637             this.intervalID = window.setInterval(function() {
11638                 Roo.bootstrap.Form.popover.unmask();
11639             }, 10000);
11640
11641             window.onwheel = function(){ return false;};
11642             
11643             (function(){ this.isMasked = true; }).defer(500, this);
11644             
11645         },
11646         
11647         unmask : function()
11648         {
11649             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11650                 return;
11651             }
11652             
11653             this.maskEl.top.setStyle('position', 'absolute');
11654             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11655             this.maskEl.top.hide();
11656
11657             this.maskEl.left.setStyle('position', 'absolute');
11658             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11659             this.maskEl.left.hide();
11660
11661             this.maskEl.bottom.setStyle('position', 'absolute');
11662             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11663             this.maskEl.bottom.hide();
11664
11665             this.maskEl.right.setStyle('position', 'absolute');
11666             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11667             this.maskEl.right.hide();
11668             
11669             this.toolTip.hide();
11670             
11671             this.toolTip.el.hide();
11672             
11673             window.onwheel = function(){ return true;};
11674             
11675             if(this.intervalID){
11676                 window.clearInterval(this.intervalID);
11677                 this.intervalID = false;
11678             }
11679             
11680             this.isMasked = false;
11681             
11682         }
11683         
11684     }
11685     
11686 });
11687
11688 /*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698 /**
11699  * @class Roo.form.VTypes
11700  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11701  * @singleton
11702  */
11703 Roo.form.VTypes = function(){
11704     // closure these in so they are only created once.
11705     var alpha = /^[a-zA-Z_]+$/;
11706     var alphanum = /^[a-zA-Z0-9_]+$/;
11707     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11708     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11709
11710     // All these messages and functions are configurable
11711     return {
11712         /**
11713          * The function used to validate email addresses
11714          * @param {String} value The email address
11715          */
11716         'email' : function(v){
11717             return email.test(v);
11718         },
11719         /**
11720          * The error text to display when the email validation function returns false
11721          * @type String
11722          */
11723         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11724         /**
11725          * The keystroke filter mask to be applied on email input
11726          * @type RegExp
11727          */
11728         'emailMask' : /[a-z0-9_\.\-@]/i,
11729
11730         /**
11731          * The function used to validate URLs
11732          * @param {String} value The URL
11733          */
11734         'url' : function(v){
11735             return url.test(v);
11736         },
11737         /**
11738          * The error text to display when the url validation function returns false
11739          * @type String
11740          */
11741         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11742         
11743         /**
11744          * The function used to validate alpha values
11745          * @param {String} value The value
11746          */
11747         'alpha' : function(v){
11748             return alpha.test(v);
11749         },
11750         /**
11751          * The error text to display when the alpha validation function returns false
11752          * @type String
11753          */
11754         'alphaText' : 'This field should only contain letters and _',
11755         /**
11756          * The keystroke filter mask to be applied on alpha input
11757          * @type RegExp
11758          */
11759         'alphaMask' : /[a-z_]/i,
11760
11761         /**
11762          * The function used to validate alphanumeric values
11763          * @param {String} value The value
11764          */
11765         'alphanum' : function(v){
11766             return alphanum.test(v);
11767         },
11768         /**
11769          * The error text to display when the alphanumeric validation function returns false
11770          * @type String
11771          */
11772         'alphanumText' : 'This field should only contain letters, numbers and _',
11773         /**
11774          * The keystroke filter mask to be applied on alphanumeric input
11775          * @type RegExp
11776          */
11777         'alphanumMask' : /[a-z0-9_]/i
11778     };
11779 }();/*
11780  * - LGPL
11781  *
11782  * Input
11783  * 
11784  */
11785
11786 /**
11787  * @class Roo.bootstrap.Input
11788  * @extends Roo.bootstrap.Component
11789  * Bootstrap Input class
11790  * @cfg {Boolean} disabled is it disabled
11791  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11792  * @cfg {String} name name of the input
11793  * @cfg {string} fieldLabel - the label associated
11794  * @cfg {string} placeholder - placeholder to put in text.
11795  * @cfg {string}  before - input group add on before
11796  * @cfg {string} after - input group add on after
11797  * @cfg {string} size - (lg|sm) or leave empty..
11798  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11799  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11800  * @cfg {Number} md colspan out of 12 for computer-sized screens
11801  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11802  * @cfg {string} value default value of the input
11803  * @cfg {Number} labelWidth set the width of label 
11804  * @cfg {Number} labellg set the width of label (1-12)
11805  * @cfg {Number} labelmd set the width of label (1-12)
11806  * @cfg {Number} labelsm set the width of label (1-12)
11807  * @cfg {Number} labelxs set the width of label (1-12)
11808  * @cfg {String} labelAlign (top|left)
11809  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11810  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11811  * @cfg {String} indicatorpos (left|right) default left
11812  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11813  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11814  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11815
11816  * @cfg {String} align (left|center|right) Default left
11817  * @cfg {Boolean} forceFeedback (true|false) Default false
11818  * 
11819  * @constructor
11820  * Create a new Input
11821  * @param {Object} config The config object
11822  */
11823
11824 Roo.bootstrap.Input = function(config){
11825     
11826     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11827     
11828     this.addEvents({
11829         /**
11830          * @event focus
11831          * Fires when this field receives input focus.
11832          * @param {Roo.form.Field} this
11833          */
11834         focus : true,
11835         /**
11836          * @event blur
11837          * Fires when this field loses input focus.
11838          * @param {Roo.form.Field} this
11839          */
11840         blur : true,
11841         /**
11842          * @event specialkey
11843          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11844          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11845          * @param {Roo.form.Field} this
11846          * @param {Roo.EventObject} e The event object
11847          */
11848         specialkey : true,
11849         /**
11850          * @event change
11851          * Fires just before the field blurs if the field value has changed.
11852          * @param {Roo.form.Field} this
11853          * @param {Mixed} newValue The new value
11854          * @param {Mixed} oldValue The original value
11855          */
11856         change : true,
11857         /**
11858          * @event invalid
11859          * Fires after the field has been marked as invalid.
11860          * @param {Roo.form.Field} this
11861          * @param {String} msg The validation message
11862          */
11863         invalid : true,
11864         /**
11865          * @event valid
11866          * Fires after the field has been validated with no errors.
11867          * @param {Roo.form.Field} this
11868          */
11869         valid : true,
11870          /**
11871          * @event keyup
11872          * Fires after the key up
11873          * @param {Roo.form.Field} this
11874          * @param {Roo.EventObject}  e The event Object
11875          */
11876         keyup : true,
11877         /**
11878          * @event paste
11879          * Fires after the user pastes into input
11880          * @param {Roo.form.Field} this
11881          * @param {Roo.EventObject}  e The event Object
11882          */
11883         paste : true
11884     });
11885 };
11886
11887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11888      /**
11889      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11890       automatic validation (defaults to "keyup").
11891      */
11892     validationEvent : "keyup",
11893      /**
11894      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11895      */
11896     validateOnBlur : true,
11897     /**
11898      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11899      */
11900     validationDelay : 250,
11901      /**
11902      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11903      */
11904     focusClass : "x-form-focus",  // not needed???
11905     
11906        
11907     /**
11908      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11909      */
11910     invalidClass : "has-warning",
11911     
11912     /**
11913      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11914      */
11915     validClass : "has-success",
11916     
11917     /**
11918      * @cfg {Boolean} hasFeedback (true|false) default true
11919      */
11920     hasFeedback : true,
11921     
11922     /**
11923      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11924      */
11925     invalidFeedbackClass : "glyphicon-warning-sign",
11926     
11927     /**
11928      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11929      */
11930     validFeedbackClass : "glyphicon-ok",
11931     
11932     /**
11933      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11934      */
11935     selectOnFocus : false,
11936     
11937      /**
11938      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11939      */
11940     maskRe : null,
11941        /**
11942      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11943      */
11944     vtype : null,
11945     
11946       /**
11947      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11948      */
11949     disableKeyFilter : false,
11950     
11951        /**
11952      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11953      */
11954     disabled : false,
11955      /**
11956      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11957      */
11958     allowBlank : true,
11959     /**
11960      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11961      */
11962     blankText : "Please complete this mandatory field",
11963     
11964      /**
11965      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11966      */
11967     minLength : 0,
11968     /**
11969      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11970      */
11971     maxLength : Number.MAX_VALUE,
11972     /**
11973      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11974      */
11975     minLengthText : "The minimum length for this field is {0}",
11976     /**
11977      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11978      */
11979     maxLengthText : "The maximum length for this field is {0}",
11980   
11981     
11982     /**
11983      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11984      * If available, this function will be called only after the basic validators all return true, and will be passed the
11985      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11986      */
11987     validator : null,
11988     /**
11989      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11990      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11991      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11992      */
11993     regex : null,
11994     /**
11995      * @cfg {String} regexText -- Depricated - use Invalid Text
11996      */
11997     regexText : "",
11998     
11999     /**
12000      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12001      */
12002     invalidText : "",
12003     
12004     
12005     
12006     autocomplete: false,
12007     
12008     
12009     fieldLabel : '',
12010     inputType : 'text',
12011     
12012     name : false,
12013     placeholder: false,
12014     before : false,
12015     after : false,
12016     size : false,
12017     hasFocus : false,
12018     preventMark: false,
12019     isFormField : true,
12020     value : '',
12021     labelWidth : 2,
12022     labelAlign : false,
12023     readOnly : false,
12024     align : false,
12025     formatedValue : false,
12026     forceFeedback : false,
12027     
12028     indicatorpos : 'left',
12029     
12030     labellg : 0,
12031     labelmd : 0,
12032     labelsm : 0,
12033     labelxs : 0,
12034     
12035     capture : '',
12036     accept : '',
12037     
12038     parentLabelAlign : function()
12039     {
12040         var parent = this;
12041         while (parent.parent()) {
12042             parent = parent.parent();
12043             if (typeof(parent.labelAlign) !='undefined') {
12044                 return parent.labelAlign;
12045             }
12046         }
12047         return 'left';
12048         
12049     },
12050     
12051     getAutoCreate : function()
12052     {
12053         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12054         
12055         var id = Roo.id();
12056         
12057         var cfg = {};
12058         
12059         if(this.inputType != 'hidden'){
12060             cfg.cls = 'form-group' //input-group
12061         }
12062         
12063         var input =  {
12064             tag: 'input',
12065             id : id,
12066             type : this.inputType,
12067             value : this.value,
12068             cls : 'form-control',
12069             placeholder : this.placeholder || '',
12070             autocomplete : this.autocomplete || 'new-password'
12071         };
12072         if (this.inputType == 'file') {
12073             input.style = 'overflow:hidden'; // why not in CSS?
12074         }
12075         
12076         if(this.capture.length){
12077             input.capture = this.capture;
12078         }
12079         
12080         if(this.accept.length){
12081             input.accept = this.accept + "/*";
12082         }
12083         
12084         if(this.align){
12085             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12086         }
12087         
12088         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12089             input.maxLength = this.maxLength;
12090         }
12091         
12092         if (this.disabled) {
12093             input.disabled=true;
12094         }
12095         
12096         if (this.readOnly) {
12097             input.readonly=true;
12098         }
12099         
12100         if (this.name) {
12101             input.name = this.name;
12102         }
12103         
12104         if (this.size) {
12105             input.cls += ' input-' + this.size;
12106         }
12107         
12108         var settings=this;
12109         ['xs','sm','md','lg'].map(function(size){
12110             if (settings[size]) {
12111                 cfg.cls += ' col-' + size + '-' + settings[size];
12112             }
12113         });
12114         
12115         var inputblock = input;
12116         
12117         var feedback = {
12118             tag: 'span',
12119             cls: 'glyphicon form-control-feedback'
12120         };
12121             
12122         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12123             
12124             inputblock = {
12125                 cls : 'has-feedback',
12126                 cn :  [
12127                     input,
12128                     feedback
12129                 ] 
12130             };  
12131         }
12132         
12133         if (this.before || this.after) {
12134             
12135             inputblock = {
12136                 cls : 'input-group',
12137                 cn :  [] 
12138             };
12139             
12140             if (this.before && typeof(this.before) == 'string') {
12141                 
12142                 inputblock.cn.push({
12143                     tag :'span',
12144                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12145                     html : this.before
12146                 });
12147             }
12148             if (this.before && typeof(this.before) == 'object') {
12149                 this.before = Roo.factory(this.before);
12150                 
12151                 inputblock.cn.push({
12152                     tag :'span',
12153                     cls : 'roo-input-before input-group-prepend   input-group-' +
12154                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12155                 });
12156             }
12157             
12158             inputblock.cn.push(input);
12159             
12160             if (this.after && typeof(this.after) == 'string') {
12161                 inputblock.cn.push({
12162                     tag :'span',
12163                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12164                     html : this.after
12165                 });
12166             }
12167             if (this.after && typeof(this.after) == 'object') {
12168                 this.after = Roo.factory(this.after);
12169                 
12170                 inputblock.cn.push({
12171                     tag :'span',
12172                     cls : 'roo-input-after input-group-append  input-group-' +
12173                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12174                 });
12175             }
12176             
12177             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178                 inputblock.cls += ' has-feedback';
12179                 inputblock.cn.push(feedback);
12180             }
12181         };
12182         var indicator = {
12183             tag : 'i',
12184             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12185             tooltip : 'This field is required'
12186         };
12187         if (this.allowBlank ) {
12188             indicator.style = this.allowBlank ? ' display:none' : '';
12189         }
12190         if (align ==='left' && this.fieldLabel.length) {
12191             
12192             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12193             
12194             cfg.cn = [
12195                 indicator,
12196                 {
12197                     tag: 'label',
12198                     'for' :  id,
12199                     cls : 'control-label col-form-label',
12200                     html : this.fieldLabel
12201
12202                 },
12203                 {
12204                     cls : "", 
12205                     cn: [
12206                         inputblock
12207                     ]
12208                 }
12209             ];
12210             
12211             var labelCfg = cfg.cn[1];
12212             var contentCfg = cfg.cn[2];
12213             
12214             if(this.indicatorpos == 'right'){
12215                 cfg.cn = [
12216                     {
12217                         tag: 'label',
12218                         'for' :  id,
12219                         cls : 'control-label col-form-label',
12220                         cn : [
12221                             {
12222                                 tag : 'span',
12223                                 html : this.fieldLabel
12224                             },
12225                             indicator
12226                         ]
12227                     },
12228                     {
12229                         cls : "",
12230                         cn: [
12231                             inputblock
12232                         ]
12233                     }
12234
12235                 ];
12236                 
12237                 labelCfg = cfg.cn[0];
12238                 contentCfg = cfg.cn[1];
12239             
12240             }
12241             
12242             if(this.labelWidth > 12){
12243                 labelCfg.style = "width: " + this.labelWidth + 'px';
12244             }
12245             
12246             if(this.labelWidth < 13 && this.labelmd == 0){
12247                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12248             }
12249             
12250             if(this.labellg > 0){
12251                 labelCfg.cls += ' col-lg-' + this.labellg;
12252                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12253             }
12254             
12255             if(this.labelmd > 0){
12256                 labelCfg.cls += ' col-md-' + this.labelmd;
12257                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12258             }
12259             
12260             if(this.labelsm > 0){
12261                 labelCfg.cls += ' col-sm-' + this.labelsm;
12262                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12263             }
12264             
12265             if(this.labelxs > 0){
12266                 labelCfg.cls += ' col-xs-' + this.labelxs;
12267                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12268             }
12269             
12270             
12271         } else if ( this.fieldLabel.length) {
12272                 
12273             
12274             
12275             cfg.cn = [
12276                 {
12277                     tag : 'i',
12278                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12279                     tooltip : 'This field is required',
12280                     style : this.allowBlank ? ' display:none' : '' 
12281                 },
12282                 {
12283                     tag: 'label',
12284                    //cls : 'input-group-addon',
12285                     html : this.fieldLabel
12286
12287                 },
12288
12289                inputblock
12290
12291            ];
12292            
12293            if(this.indicatorpos == 'right'){
12294        
12295                 cfg.cn = [
12296                     {
12297                         tag: 'label',
12298                        //cls : 'input-group-addon',
12299                         html : this.fieldLabel
12300
12301                     },
12302                     {
12303                         tag : 'i',
12304                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12305                         tooltip : 'This field is required',
12306                         style : this.allowBlank ? ' display:none' : '' 
12307                     },
12308
12309                    inputblock
12310
12311                ];
12312
12313             }
12314
12315         } else {
12316             
12317             cfg.cn = [
12318
12319                     inputblock
12320
12321             ];
12322                 
12323                 
12324         };
12325         
12326         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12327            cfg.cls += ' navbar-form';
12328         }
12329         
12330         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12331             // on BS4 we do this only if not form 
12332             cfg.cls += ' navbar-form';
12333             cfg.tag = 'li';
12334         }
12335         
12336         return cfg;
12337         
12338     },
12339     /**
12340      * return the real input element.
12341      */
12342     inputEl: function ()
12343     {
12344         return this.el.select('input.form-control',true).first();
12345     },
12346     
12347     tooltipEl : function()
12348     {
12349         return this.inputEl();
12350     },
12351     
12352     indicatorEl : function()
12353     {
12354         if (Roo.bootstrap.version == 4) {
12355             return false; // not enabled in v4 yet.
12356         }
12357         
12358         var indicator = this.el.select('i.roo-required-indicator',true).first();
12359         
12360         if(!indicator){
12361             return false;
12362         }
12363         
12364         return indicator;
12365         
12366     },
12367     
12368     setDisabled : function(v)
12369     {
12370         var i  = this.inputEl().dom;
12371         if (!v) {
12372             i.removeAttribute('disabled');
12373             return;
12374             
12375         }
12376         i.setAttribute('disabled','true');
12377     },
12378     initEvents : function()
12379     {
12380           
12381         this.inputEl().on("keydown" , this.fireKey,  this);
12382         this.inputEl().on("focus", this.onFocus,  this);
12383         this.inputEl().on("blur", this.onBlur,  this);
12384         
12385         this.inputEl().relayEvent('keyup', this);
12386         this.inputEl().relayEvent('paste', this);
12387         
12388         this.indicator = this.indicatorEl();
12389         
12390         if(this.indicator){
12391             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12392         }
12393  
12394         // reference to original value for reset
12395         this.originalValue = this.getValue();
12396         //Roo.form.TextField.superclass.initEvents.call(this);
12397         if(this.validationEvent == 'keyup'){
12398             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12399             this.inputEl().on('keyup', this.filterValidation, this);
12400         }
12401         else if(this.validationEvent !== false){
12402             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12403         }
12404         
12405         if(this.selectOnFocus){
12406             this.on("focus", this.preFocus, this);
12407             
12408         }
12409         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12410             this.inputEl().on("keypress", this.filterKeys, this);
12411         } else {
12412             this.inputEl().relayEvent('keypress', this);
12413         }
12414        /* if(this.grow){
12415             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12416             this.el.on("click", this.autoSize,  this);
12417         }
12418         */
12419         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12420             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12421         }
12422         
12423         if (typeof(this.before) == 'object') {
12424             this.before.render(this.el.select('.roo-input-before',true).first());
12425         }
12426         if (typeof(this.after) == 'object') {
12427             this.after.render(this.el.select('.roo-input-after',true).first());
12428         }
12429         
12430         this.inputEl().on('change', this.onChange, this);
12431         
12432     },
12433     filterValidation : function(e){
12434         if(!e.isNavKeyPress()){
12435             this.validationTask.delay(this.validationDelay);
12436         }
12437     },
12438      /**
12439      * Validates the field value
12440      * @return {Boolean} True if the value is valid, else false
12441      */
12442     validate : function(){
12443         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12444         if(this.disabled || this.validateValue(this.getRawValue())){
12445             this.markValid();
12446             return true;
12447         }
12448         
12449         this.markInvalid();
12450         return false;
12451     },
12452     
12453     
12454     /**
12455      * Validates a value according to the field's validation rules and marks the field as invalid
12456      * if the validation fails
12457      * @param {Mixed} value The value to validate
12458      * @return {Boolean} True if the value is valid, else false
12459      */
12460     validateValue : function(value)
12461     {
12462         if(this.getVisibilityEl().hasClass('hidden')){
12463             return true;
12464         }
12465         
12466         if(value.length < 1)  { // if it's blank
12467             if(this.allowBlank){
12468                 return true;
12469             }
12470             return false;
12471         }
12472         
12473         if(value.length < this.minLength){
12474             return false;
12475         }
12476         if(value.length > this.maxLength){
12477             return false;
12478         }
12479         if(this.vtype){
12480             var vt = Roo.form.VTypes;
12481             if(!vt[this.vtype](value, this)){
12482                 return false;
12483             }
12484         }
12485         if(typeof this.validator == "function"){
12486             var msg = this.validator(value);
12487             if(msg !== true){
12488                 return false;
12489             }
12490             if (typeof(msg) == 'string') {
12491                 this.invalidText = msg;
12492             }
12493         }
12494         
12495         if(this.regex && !this.regex.test(value)){
12496             return false;
12497         }
12498         
12499         return true;
12500     },
12501     
12502      // private
12503     fireKey : function(e){
12504         //Roo.log('field ' + e.getKey());
12505         if(e.isNavKeyPress()){
12506             this.fireEvent("specialkey", this, e);
12507         }
12508     },
12509     focus : function (selectText){
12510         if(this.rendered){
12511             this.inputEl().focus();
12512             if(selectText === true){
12513                 this.inputEl().dom.select();
12514             }
12515         }
12516         return this;
12517     } ,
12518     
12519     onFocus : function(){
12520         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12521            // this.el.addClass(this.focusClass);
12522         }
12523         if(!this.hasFocus){
12524             this.hasFocus = true;
12525             this.startValue = this.getValue();
12526             this.fireEvent("focus", this);
12527         }
12528     },
12529     
12530     beforeBlur : Roo.emptyFn,
12531
12532     
12533     // private
12534     onBlur : function(){
12535         this.beforeBlur();
12536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12537             //this.el.removeClass(this.focusClass);
12538         }
12539         this.hasFocus = false;
12540         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12541             this.validate();
12542         }
12543         var v = this.getValue();
12544         if(String(v) !== String(this.startValue)){
12545             this.fireEvent('change', this, v, this.startValue);
12546         }
12547         this.fireEvent("blur", this);
12548     },
12549     
12550     onChange : function(e)
12551     {
12552         var v = this.getValue();
12553         if(String(v) !== String(this.startValue)){
12554             this.fireEvent('change', this, v, this.startValue);
12555         }
12556         
12557     },
12558     
12559     /**
12560      * Resets the current field value to the originally loaded value and clears any validation messages
12561      */
12562     reset : function(){
12563         this.setValue(this.originalValue);
12564         this.validate();
12565     },
12566      /**
12567      * Returns the name of the field
12568      * @return {Mixed} name The name field
12569      */
12570     getName: function(){
12571         return this.name;
12572     },
12573      /**
12574      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12575      * @return {Mixed} value The field value
12576      */
12577     getValue : function(){
12578         
12579         var v = this.inputEl().getValue();
12580         
12581         return v;
12582     },
12583     /**
12584      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12585      * @return {Mixed} value The field value
12586      */
12587     getRawValue : function(){
12588         var v = this.inputEl().getValue();
12589         
12590         return v;
12591     },
12592     
12593     /**
12594      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12595      * @param {Mixed} value The value to set
12596      */
12597     setRawValue : function(v){
12598         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12599     },
12600     
12601     selectText : function(start, end){
12602         var v = this.getRawValue();
12603         if(v.length > 0){
12604             start = start === undefined ? 0 : start;
12605             end = end === undefined ? v.length : end;
12606             var d = this.inputEl().dom;
12607             if(d.setSelectionRange){
12608                 d.setSelectionRange(start, end);
12609             }else if(d.createTextRange){
12610                 var range = d.createTextRange();
12611                 range.moveStart("character", start);
12612                 range.moveEnd("character", v.length-end);
12613                 range.select();
12614             }
12615         }
12616     },
12617     
12618     /**
12619      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12620      * @param {Mixed} value The value to set
12621      */
12622     setValue : function(v){
12623         this.value = v;
12624         if(this.rendered){
12625             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12626             this.validate();
12627         }
12628     },
12629     
12630     /*
12631     processValue : function(value){
12632         if(this.stripCharsRe){
12633             var newValue = value.replace(this.stripCharsRe, '');
12634             if(newValue !== value){
12635                 this.setRawValue(newValue);
12636                 return newValue;
12637             }
12638         }
12639         return value;
12640     },
12641   */
12642     preFocus : function(){
12643         
12644         if(this.selectOnFocus){
12645             this.inputEl().dom.select();
12646         }
12647     },
12648     filterKeys : function(e){
12649         var k = e.getKey();
12650         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12651             return;
12652         }
12653         var c = e.getCharCode(), cc = String.fromCharCode(c);
12654         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12655             return;
12656         }
12657         if(!this.maskRe.test(cc)){
12658             e.stopEvent();
12659         }
12660     },
12661      /**
12662      * Clear any invalid styles/messages for this field
12663      */
12664     clearInvalid : function(){
12665         
12666         if(!this.el || this.preventMark){ // not rendered
12667             return;
12668         }
12669         
12670         
12671         this.el.removeClass([this.invalidClass, 'is-invalid']);
12672         
12673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12674             
12675             var feedback = this.el.select('.form-control-feedback', true).first();
12676             
12677             if(feedback){
12678                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12679             }
12680             
12681         }
12682         
12683         if(this.indicator){
12684             this.indicator.removeClass('visible');
12685             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12686         }
12687         
12688         this.fireEvent('valid', this);
12689     },
12690     
12691      /**
12692      * Mark this field as valid
12693      */
12694     markValid : function()
12695     {
12696         if(!this.el  || this.preventMark){ // not rendered...
12697             return;
12698         }
12699         
12700         this.el.removeClass([this.invalidClass, this.validClass]);
12701         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12702
12703         var feedback = this.el.select('.form-control-feedback', true).first();
12704             
12705         if(feedback){
12706             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12707         }
12708         
12709         if(this.indicator){
12710             this.indicator.removeClass('visible');
12711             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12712         }
12713         
12714         if(this.disabled){
12715             return;
12716         }
12717         
12718            
12719         if(this.allowBlank && !this.getRawValue().length){
12720             return;
12721         }
12722         if (Roo.bootstrap.version == 3) {
12723             this.el.addClass(this.validClass);
12724         } else {
12725             this.inputEl().addClass('is-valid');
12726         }
12727
12728         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12729             
12730             var feedback = this.el.select('.form-control-feedback', true).first();
12731             
12732             if(feedback){
12733                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12734                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12735             }
12736             
12737         }
12738         
12739         this.fireEvent('valid', this);
12740     },
12741     
12742      /**
12743      * Mark this field as invalid
12744      * @param {String} msg The validation message
12745      */
12746     markInvalid : function(msg)
12747     {
12748         if(!this.el  || this.preventMark){ // not rendered
12749             return;
12750         }
12751         
12752         this.el.removeClass([this.invalidClass, this.validClass]);
12753         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12754         
12755         var feedback = this.el.select('.form-control-feedback', true).first();
12756             
12757         if(feedback){
12758             this.el.select('.form-control-feedback', true).first().removeClass(
12759                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12760         }
12761
12762         if(this.disabled){
12763             return;
12764         }
12765         
12766         if(this.allowBlank && !this.getRawValue().length){
12767             return;
12768         }
12769         
12770         if(this.indicator){
12771             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12772             this.indicator.addClass('visible');
12773         }
12774         if (Roo.bootstrap.version == 3) {
12775             this.el.addClass(this.invalidClass);
12776         } else {
12777             this.inputEl().addClass('is-invalid');
12778         }
12779         
12780         
12781         
12782         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12783             
12784             var feedback = this.el.select('.form-control-feedback', true).first();
12785             
12786             if(feedback){
12787                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12788                 
12789                 if(this.getValue().length || this.forceFeedback){
12790                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12791                 }
12792                 
12793             }
12794             
12795         }
12796         
12797         this.fireEvent('invalid', this, msg);
12798     },
12799     // private
12800     SafariOnKeyDown : function(event)
12801     {
12802         // this is a workaround for a password hang bug on chrome/ webkit.
12803         if (this.inputEl().dom.type != 'password') {
12804             return;
12805         }
12806         
12807         var isSelectAll = false;
12808         
12809         if(this.inputEl().dom.selectionEnd > 0){
12810             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12811         }
12812         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12813             event.preventDefault();
12814             this.setValue('');
12815             return;
12816         }
12817         
12818         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12819             
12820             event.preventDefault();
12821             // this is very hacky as keydown always get's upper case.
12822             //
12823             var cc = String.fromCharCode(event.getCharCode());
12824             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12825             
12826         }
12827     },
12828     adjustWidth : function(tag, w){
12829         tag = tag.toLowerCase();
12830         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12831             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12832                 if(tag == 'input'){
12833                     return w + 2;
12834                 }
12835                 if(tag == 'textarea'){
12836                     return w-2;
12837                 }
12838             }else if(Roo.isOpera){
12839                 if(tag == 'input'){
12840                     return w + 2;
12841                 }
12842                 if(tag == 'textarea'){
12843                     return w-2;
12844                 }
12845             }
12846         }
12847         return w;
12848     },
12849     
12850     setFieldLabel : function(v)
12851     {
12852         if(!this.rendered){
12853             return;
12854         }
12855         
12856         if(this.indicatorEl()){
12857             var ar = this.el.select('label > span',true);
12858             
12859             if (ar.elements.length) {
12860                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12861                 this.fieldLabel = v;
12862                 return;
12863             }
12864             
12865             var br = this.el.select('label',true);
12866             
12867             if(br.elements.length) {
12868                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12869                 this.fieldLabel = v;
12870                 return;
12871             }
12872             
12873             Roo.log('Cannot Found any of label > span || label in input');
12874             return;
12875         }
12876         
12877         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878         this.fieldLabel = v;
12879         
12880         
12881     }
12882 });
12883
12884  
12885 /*
12886  * - LGPL
12887  *
12888  * Input
12889  * 
12890  */
12891
12892 /**
12893  * @class Roo.bootstrap.TextArea
12894  * @extends Roo.bootstrap.Input
12895  * Bootstrap TextArea class
12896  * @cfg {Number} cols Specifies the visible width of a text area
12897  * @cfg {Number} rows Specifies the visible number of lines in a text area
12898  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12899  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12900  * @cfg {string} html text
12901  * 
12902  * @constructor
12903  * Create a new TextArea
12904  * @param {Object} config The config object
12905  */
12906
12907 Roo.bootstrap.TextArea = function(config){
12908     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12909    
12910 };
12911
12912 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12913      
12914     cols : false,
12915     rows : 5,
12916     readOnly : false,
12917     warp : 'soft',
12918     resize : false,
12919     value: false,
12920     html: false,
12921     
12922     getAutoCreate : function(){
12923         
12924         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12925         
12926         var id = Roo.id();
12927         
12928         var cfg = {};
12929         
12930         if(this.inputType != 'hidden'){
12931             cfg.cls = 'form-group' //input-group
12932         }
12933         
12934         var input =  {
12935             tag: 'textarea',
12936             id : id,
12937             warp : this.warp,
12938             rows : this.rows,
12939             value : this.value || '',
12940             html: this.html || '',
12941             cls : 'form-control',
12942             placeholder : this.placeholder || '' 
12943             
12944         };
12945         
12946         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12947             input.maxLength = this.maxLength;
12948         }
12949         
12950         if(this.resize){
12951             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12952         }
12953         
12954         if(this.cols){
12955             input.cols = this.cols;
12956         }
12957         
12958         if (this.readOnly) {
12959             input.readonly = true;
12960         }
12961         
12962         if (this.name) {
12963             input.name = this.name;
12964         }
12965         
12966         if (this.size) {
12967             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12968         }
12969         
12970         var settings=this;
12971         ['xs','sm','md','lg'].map(function(size){
12972             if (settings[size]) {
12973                 cfg.cls += ' col-' + size + '-' + settings[size];
12974             }
12975         });
12976         
12977         var inputblock = input;
12978         
12979         if(this.hasFeedback && !this.allowBlank){
12980             
12981             var feedback = {
12982                 tag: 'span',
12983                 cls: 'glyphicon form-control-feedback'
12984             };
12985
12986             inputblock = {
12987                 cls : 'has-feedback',
12988                 cn :  [
12989                     input,
12990                     feedback
12991                 ] 
12992             };  
12993         }
12994         
12995         
12996         if (this.before || this.after) {
12997             
12998             inputblock = {
12999                 cls : 'input-group',
13000                 cn :  [] 
13001             };
13002             if (this.before) {
13003                 inputblock.cn.push({
13004                     tag :'span',
13005                     cls : 'input-group-addon',
13006                     html : this.before
13007                 });
13008             }
13009             
13010             inputblock.cn.push(input);
13011             
13012             if(this.hasFeedback && !this.allowBlank){
13013                 inputblock.cls += ' has-feedback';
13014                 inputblock.cn.push(feedback);
13015             }
13016             
13017             if (this.after) {
13018                 inputblock.cn.push({
13019                     tag :'span',
13020                     cls : 'input-group-addon',
13021                     html : this.after
13022                 });
13023             }
13024             
13025         }
13026         
13027         if (align ==='left' && this.fieldLabel.length) {
13028             cfg.cn = [
13029                 {
13030                     tag: 'label',
13031                     'for' :  id,
13032                     cls : 'control-label',
13033                     html : this.fieldLabel
13034                 },
13035                 {
13036                     cls : "",
13037                     cn: [
13038                         inputblock
13039                     ]
13040                 }
13041
13042             ];
13043             
13044             if(this.labelWidth > 12){
13045                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13046             }
13047
13048             if(this.labelWidth < 13 && this.labelmd == 0){
13049                 this.labelmd = this.labelWidth;
13050             }
13051
13052             if(this.labellg > 0){
13053                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13054                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13055             }
13056
13057             if(this.labelmd > 0){
13058                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13059                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13060             }
13061
13062             if(this.labelsm > 0){
13063                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13064                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13065             }
13066
13067             if(this.labelxs > 0){
13068                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13069                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13070             }
13071             
13072         } else if ( this.fieldLabel.length) {
13073             cfg.cn = [
13074
13075                {
13076                    tag: 'label',
13077                    //cls : 'input-group-addon',
13078                    html : this.fieldLabel
13079
13080                },
13081
13082                inputblock
13083
13084            ];
13085
13086         } else {
13087
13088             cfg.cn = [
13089
13090                 inputblock
13091
13092             ];
13093                 
13094         }
13095         
13096         if (this.disabled) {
13097             input.disabled=true;
13098         }
13099         
13100         return cfg;
13101         
13102     },
13103     /**
13104      * return the real textarea element.
13105      */
13106     inputEl: function ()
13107     {
13108         return this.el.select('textarea.form-control',true).first();
13109     },
13110     
13111     /**
13112      * Clear any invalid styles/messages for this field
13113      */
13114     clearInvalid : function()
13115     {
13116         
13117         if(!this.el || this.preventMark){ // not rendered
13118             return;
13119         }
13120         
13121         var label = this.el.select('label', true).first();
13122         var icon = this.el.select('i.fa-star', true).first();
13123         
13124         if(label && icon){
13125             icon.remove();
13126         }
13127         this.el.removeClass( this.validClass);
13128         this.inputEl().removeClass('is-invalid');
13129          
13130         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13131             
13132             var feedback = this.el.select('.form-control-feedback', true).first();
13133             
13134             if(feedback){
13135                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13136             }
13137             
13138         }
13139         
13140         this.fireEvent('valid', this);
13141     },
13142     
13143      /**
13144      * Mark this field as valid
13145      */
13146     markValid : function()
13147     {
13148         if(!this.el  || this.preventMark){ // not rendered
13149             return;
13150         }
13151         
13152         this.el.removeClass([this.invalidClass, this.validClass]);
13153         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13154         
13155         var feedback = this.el.select('.form-control-feedback', true).first();
13156             
13157         if(feedback){
13158             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13159         }
13160
13161         if(this.disabled || this.allowBlank){
13162             return;
13163         }
13164         
13165         var label = this.el.select('label', true).first();
13166         var icon = this.el.select('i.fa-star', true).first();
13167         
13168         if(label && icon){
13169             icon.remove();
13170         }
13171         if (Roo.bootstrap.version == 3) {
13172             this.el.addClass(this.validClass);
13173         } else {
13174             this.inputEl().addClass('is-valid');
13175         }
13176         
13177         
13178         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13179             
13180             var feedback = this.el.select('.form-control-feedback', true).first();
13181             
13182             if(feedback){
13183                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13185             }
13186             
13187         }
13188         
13189         this.fireEvent('valid', this);
13190     },
13191     
13192      /**
13193      * Mark this field as invalid
13194      * @param {String} msg The validation message
13195      */
13196     markInvalid : function(msg)
13197     {
13198         if(!this.el  || this.preventMark){ // not rendered
13199             return;
13200         }
13201         
13202         this.el.removeClass([this.invalidClass, this.validClass]);
13203         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13204         
13205         var feedback = this.el.select('.form-control-feedback', true).first();
13206             
13207         if(feedback){
13208             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13209         }
13210
13211         if(this.disabled || this.allowBlank){
13212             return;
13213         }
13214         
13215         var label = this.el.select('label', true).first();
13216         var icon = this.el.select('i.fa-star', true).first();
13217         
13218         if(!this.getValue().length && label && !icon){
13219             this.el.createChild({
13220                 tag : 'i',
13221                 cls : 'text-danger fa fa-lg fa-star',
13222                 tooltip : 'This field is required',
13223                 style : 'margin-right:5px;'
13224             }, label, true);
13225         }
13226         
13227         if (Roo.bootstrap.version == 3) {
13228             this.el.addClass(this.invalidClass);
13229         } else {
13230             this.inputEl().addClass('is-invalid');
13231         }
13232         
13233         // fixme ... this may be depricated need to test..
13234         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13235             
13236             var feedback = this.el.select('.form-control-feedback', true).first();
13237             
13238             if(feedback){
13239                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13240                 
13241                 if(this.getValue().length || this.forceFeedback){
13242                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13243                 }
13244                 
13245             }
13246             
13247         }
13248         
13249         this.fireEvent('invalid', this, msg);
13250     }
13251 });
13252
13253  
13254 /*
13255  * - LGPL
13256  *
13257  * trigger field - base class for combo..
13258  * 
13259  */
13260  
13261 /**
13262  * @class Roo.bootstrap.TriggerField
13263  * @extends Roo.bootstrap.Input
13264  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13265  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13266  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13267  * for which you can provide a custom implementation.  For example:
13268  * <pre><code>
13269 var trigger = new Roo.bootstrap.TriggerField();
13270 trigger.onTriggerClick = myTriggerFn;
13271 trigger.applyTo('my-field');
13272 </code></pre>
13273  *
13274  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13275  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13276  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13277  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13278  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13279
13280  * @constructor
13281  * Create a new TriggerField.
13282  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13283  * to the base TextField)
13284  */
13285 Roo.bootstrap.TriggerField = function(config){
13286     this.mimicing = false;
13287     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13288 };
13289
13290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13291     /**
13292      * @cfg {String} triggerClass A CSS class to apply to the trigger
13293      */
13294      /**
13295      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13296      */
13297     hideTrigger:false,
13298
13299     /**
13300      * @cfg {Boolean} removable (true|false) special filter default false
13301      */
13302     removable : false,
13303     
13304     /** @cfg {Boolean} grow @hide */
13305     /** @cfg {Number} growMin @hide */
13306     /** @cfg {Number} growMax @hide */
13307
13308     /**
13309      * @hide 
13310      * @method
13311      */
13312     autoSize: Roo.emptyFn,
13313     // private
13314     monitorTab : true,
13315     // private
13316     deferHeight : true,
13317
13318     
13319     actionMode : 'wrap',
13320     
13321     caret : false,
13322     
13323     
13324     getAutoCreate : function(){
13325        
13326         var align = this.labelAlign || this.parentLabelAlign();
13327         
13328         var id = Roo.id();
13329         
13330         var cfg = {
13331             cls: 'form-group' //input-group
13332         };
13333         
13334         
13335         var input =  {
13336             tag: 'input',
13337             id : id,
13338             type : this.inputType,
13339             cls : 'form-control',
13340             autocomplete: 'new-password',
13341             placeholder : this.placeholder || '' 
13342             
13343         };
13344         if (this.name) {
13345             input.name = this.name;
13346         }
13347         if (this.size) {
13348             input.cls += ' input-' + this.size;
13349         }
13350         
13351         if (this.disabled) {
13352             input.disabled=true;
13353         }
13354         
13355         var inputblock = input;
13356         
13357         if(this.hasFeedback && !this.allowBlank){
13358             
13359             var feedback = {
13360                 tag: 'span',
13361                 cls: 'glyphicon form-control-feedback'
13362             };
13363             
13364             if(this.removable && !this.editable  ){
13365                 inputblock = {
13366                     cls : 'has-feedback',
13367                     cn :  [
13368                         inputblock,
13369                         {
13370                             tag: 'button',
13371                             html : 'x',
13372                             cls : 'roo-combo-removable-btn close'
13373                         },
13374                         feedback
13375                     ] 
13376                 };
13377             } else {
13378                 inputblock = {
13379                     cls : 'has-feedback',
13380                     cn :  [
13381                         inputblock,
13382                         feedback
13383                     ] 
13384                 };
13385             }
13386
13387         } else {
13388             if(this.removable && !this.editable ){
13389                 inputblock = {
13390                     cls : 'roo-removable',
13391                     cn :  [
13392                         inputblock,
13393                         {
13394                             tag: 'button',
13395                             html : 'x',
13396                             cls : 'roo-combo-removable-btn close'
13397                         }
13398                     ] 
13399                 };
13400             }
13401         }
13402         
13403         if (this.before || this.after) {
13404             
13405             inputblock = {
13406                 cls : 'input-group',
13407                 cn :  [] 
13408             };
13409             if (this.before) {
13410                 inputblock.cn.push({
13411                     tag :'span',
13412                     cls : 'input-group-addon input-group-prepend input-group-text',
13413                     html : this.before
13414                 });
13415             }
13416             
13417             inputblock.cn.push(input);
13418             
13419             if(this.hasFeedback && !this.allowBlank){
13420                 inputblock.cls += ' has-feedback';
13421                 inputblock.cn.push(feedback);
13422             }
13423             
13424             if (this.after) {
13425                 inputblock.cn.push({
13426                     tag :'span',
13427                     cls : 'input-group-addon input-group-append input-group-text',
13428                     html : this.after
13429                 });
13430             }
13431             
13432         };
13433         
13434       
13435         
13436         var ibwrap = inputblock;
13437         
13438         if(this.multiple){
13439             ibwrap = {
13440                 tag: 'ul',
13441                 cls: 'roo-select2-choices',
13442                 cn:[
13443                     {
13444                         tag: 'li',
13445                         cls: 'roo-select2-search-field',
13446                         cn: [
13447
13448                             inputblock
13449                         ]
13450                     }
13451                 ]
13452             };
13453                 
13454         }
13455         
13456         var combobox = {
13457             cls: 'roo-select2-container input-group',
13458             cn: [
13459                  {
13460                     tag: 'input',
13461                     type : 'hidden',
13462                     cls: 'form-hidden-field'
13463                 },
13464                 ibwrap
13465             ]
13466         };
13467         
13468         if(!this.multiple && this.showToggleBtn){
13469             
13470             var caret = {
13471                         tag: 'span',
13472                         cls: 'caret'
13473              };
13474             if (this.caret != false) {
13475                 caret = {
13476                      tag: 'i',
13477                      cls: 'fa fa-' + this.caret
13478                 };
13479                 
13480             }
13481             
13482             combobox.cn.push({
13483                 tag :'span',
13484                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13485                 cn : [
13486                     Roo.bootstrap.version == 3 ? caret : '',
13487                     {
13488                         tag: 'span',
13489                         cls: 'combobox-clear',
13490                         cn  : [
13491                             {
13492                                 tag : 'i',
13493                                 cls: 'icon-remove'
13494                             }
13495                         ]
13496                     }
13497                 ]
13498
13499             })
13500         }
13501         
13502         if(this.multiple){
13503             combobox.cls += ' roo-select2-container-multi';
13504         }
13505          var indicator = {
13506             tag : 'i',
13507             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13508             tooltip : 'This field is required'
13509         };
13510         if (Roo.bootstrap.version == 4) {
13511             indicator = {
13512                 tag : 'i',
13513                 style : 'display:none'
13514             };
13515         }
13516         
13517         
13518         if (align ==='left' && this.fieldLabel.length) {
13519             
13520             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13521
13522             cfg.cn = [
13523                 indicator,
13524                 {
13525                     tag: 'label',
13526                     'for' :  id,
13527                     cls : 'control-label',
13528                     html : this.fieldLabel
13529
13530                 },
13531                 {
13532                     cls : "", 
13533                     cn: [
13534                         combobox
13535                     ]
13536                 }
13537
13538             ];
13539             
13540             var labelCfg = cfg.cn[1];
13541             var contentCfg = cfg.cn[2];
13542             
13543             if(this.indicatorpos == 'right'){
13544                 cfg.cn = [
13545                     {
13546                         tag: 'label',
13547                         'for' :  id,
13548                         cls : 'control-label',
13549                         cn : [
13550                             {
13551                                 tag : 'span',
13552                                 html : this.fieldLabel
13553                             },
13554                             indicator
13555                         ]
13556                     },
13557                     {
13558                         cls : "", 
13559                         cn: [
13560                             combobox
13561                         ]
13562                     }
13563
13564                 ];
13565                 
13566                 labelCfg = cfg.cn[0];
13567                 contentCfg = cfg.cn[1];
13568             }
13569             
13570             if(this.labelWidth > 12){
13571                 labelCfg.style = "width: " + this.labelWidth + 'px';
13572             }
13573             
13574             if(this.labelWidth < 13 && this.labelmd == 0){
13575                 this.labelmd = this.labelWidth;
13576             }
13577             
13578             if(this.labellg > 0){
13579                 labelCfg.cls += ' col-lg-' + this.labellg;
13580                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13581             }
13582             
13583             if(this.labelmd > 0){
13584                 labelCfg.cls += ' col-md-' + this.labelmd;
13585                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13586             }
13587             
13588             if(this.labelsm > 0){
13589                 labelCfg.cls += ' col-sm-' + this.labelsm;
13590                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13591             }
13592             
13593             if(this.labelxs > 0){
13594                 labelCfg.cls += ' col-xs-' + this.labelxs;
13595                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13596             }
13597             
13598         } else if ( this.fieldLabel.length) {
13599 //                Roo.log(" label");
13600             cfg.cn = [
13601                 indicator,
13602                {
13603                    tag: 'label',
13604                    //cls : 'input-group-addon',
13605                    html : this.fieldLabel
13606
13607                },
13608
13609                combobox
13610
13611             ];
13612             
13613             if(this.indicatorpos == 'right'){
13614                 
13615                 cfg.cn = [
13616                     {
13617                        tag: 'label',
13618                        cn : [
13619                            {
13620                                tag : 'span',
13621                                html : this.fieldLabel
13622                            },
13623                            indicator
13624                        ]
13625
13626                     },
13627                     combobox
13628
13629                 ];
13630
13631             }
13632
13633         } else {
13634             
13635 //                Roo.log(" no label && no align");
13636                 cfg = combobox
13637                      
13638                 
13639         }
13640         
13641         var settings=this;
13642         ['xs','sm','md','lg'].map(function(size){
13643             if (settings[size]) {
13644                 cfg.cls += ' col-' + size + '-' + settings[size];
13645             }
13646         });
13647         
13648         return cfg;
13649         
13650     },
13651     
13652     
13653     
13654     // private
13655     onResize : function(w, h){
13656 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13657 //        if(typeof w == 'number'){
13658 //            var x = w - this.trigger.getWidth();
13659 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13660 //            this.trigger.setStyle('left', x+'px');
13661 //        }
13662     },
13663
13664     // private
13665     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13666
13667     // private
13668     getResizeEl : function(){
13669         return this.inputEl();
13670     },
13671
13672     // private
13673     getPositionEl : function(){
13674         return this.inputEl();
13675     },
13676
13677     // private
13678     alignErrorIcon : function(){
13679         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13680     },
13681
13682     // private
13683     initEvents : function(){
13684         
13685         this.createList();
13686         
13687         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13688         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13689         if(!this.multiple && this.showToggleBtn){
13690             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13691             if(this.hideTrigger){
13692                 this.trigger.setDisplayed(false);
13693             }
13694             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13695         }
13696         
13697         if(this.multiple){
13698             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13699         }
13700         
13701         if(this.removable && !this.editable && !this.tickable){
13702             var close = this.closeTriggerEl();
13703             
13704             if(close){
13705                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13706                 close.on('click', this.removeBtnClick, this, close);
13707             }
13708         }
13709         
13710         //this.trigger.addClassOnOver('x-form-trigger-over');
13711         //this.trigger.addClassOnClick('x-form-trigger-click');
13712         
13713         //if(!this.width){
13714         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13715         //}
13716     },
13717     
13718     closeTriggerEl : function()
13719     {
13720         var close = this.el.select('.roo-combo-removable-btn', true).first();
13721         return close ? close : false;
13722     },
13723     
13724     removeBtnClick : function(e, h, el)
13725     {
13726         e.preventDefault();
13727         
13728         if(this.fireEvent("remove", this) !== false){
13729             this.reset();
13730             this.fireEvent("afterremove", this)
13731         }
13732     },
13733     
13734     createList : function()
13735     {
13736         this.list = Roo.get(document.body).createChild({
13737             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13738             cls: 'typeahead typeahead-long dropdown-menu shadow',
13739             style: 'display:none'
13740         });
13741         
13742         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13743         
13744     },
13745
13746     // private
13747     initTrigger : function(){
13748        
13749     },
13750
13751     // private
13752     onDestroy : function(){
13753         if(this.trigger){
13754             this.trigger.removeAllListeners();
13755           //  this.trigger.remove();
13756         }
13757         //if(this.wrap){
13758         //    this.wrap.remove();
13759         //}
13760         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13761     },
13762
13763     // private
13764     onFocus : function(){
13765         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13766         /*
13767         if(!this.mimicing){
13768             this.wrap.addClass('x-trigger-wrap-focus');
13769             this.mimicing = true;
13770             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13771             if(this.monitorTab){
13772                 this.el.on("keydown", this.checkTab, this);
13773             }
13774         }
13775         */
13776     },
13777
13778     // private
13779     checkTab : function(e){
13780         if(e.getKey() == e.TAB){
13781             this.triggerBlur();
13782         }
13783     },
13784
13785     // private
13786     onBlur : function(){
13787         // do nothing
13788     },
13789
13790     // private
13791     mimicBlur : function(e, t){
13792         /*
13793         if(!this.wrap.contains(t) && this.validateBlur()){
13794             this.triggerBlur();
13795         }
13796         */
13797     },
13798
13799     // private
13800     triggerBlur : function(){
13801         this.mimicing = false;
13802         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13803         if(this.monitorTab){
13804             this.el.un("keydown", this.checkTab, this);
13805         }
13806         //this.wrap.removeClass('x-trigger-wrap-focus');
13807         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13808     },
13809
13810     // private
13811     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13812     validateBlur : function(e, t){
13813         return true;
13814     },
13815
13816     // private
13817     onDisable : function(){
13818         this.inputEl().dom.disabled = true;
13819         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13820         //if(this.wrap){
13821         //    this.wrap.addClass('x-item-disabled');
13822         //}
13823     },
13824
13825     // private
13826     onEnable : function(){
13827         this.inputEl().dom.disabled = false;
13828         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13829         //if(this.wrap){
13830         //    this.el.removeClass('x-item-disabled');
13831         //}
13832     },
13833
13834     // private
13835     onShow : function(){
13836         var ae = this.getActionEl();
13837         
13838         if(ae){
13839             ae.dom.style.display = '';
13840             ae.dom.style.visibility = 'visible';
13841         }
13842     },
13843
13844     // private
13845     
13846     onHide : function(){
13847         var ae = this.getActionEl();
13848         ae.dom.style.display = 'none';
13849     },
13850
13851     /**
13852      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13853      * by an implementing function.
13854      * @method
13855      * @param {EventObject} e
13856      */
13857     onTriggerClick : Roo.emptyFn
13858 });
13859  
13860 /*
13861 * Licence: LGPL
13862 */
13863
13864 /**
13865  * @class Roo.bootstrap.CardUploader
13866  * @extends Roo.bootstrap.Button
13867  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13868  * @cfg {Number} errorTimeout default 3000
13869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13870  * @cfg {Array}  html The button text.
13871
13872  *
13873  * @constructor
13874  * Create a new CardUploader
13875  * @param {Object} config The config object
13876  */
13877
13878 Roo.bootstrap.CardUploader = function(config){
13879     
13880  
13881     
13882     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13883     
13884     
13885     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13886         return r.data.id
13887      });
13888     
13889      this.addEvents({
13890          // raw events
13891         /**
13892          * @event preview
13893          * When a image is clicked on - and needs to display a slideshow or similar..
13894          * @param {Roo.bootstrap.Card} this
13895          * @param {Object} The image information data 
13896          *
13897          */
13898         'preview' : true,
13899          /**
13900          * @event download
13901          * When a the download link is clicked
13902          * @param {Roo.bootstrap.Card} this
13903          * @param {Object} The image information data  contains 
13904          */
13905         'download' : true
13906         
13907     });
13908 };
13909  
13910 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13911     
13912      
13913     errorTimeout : 3000,
13914      
13915     images : false,
13916    
13917     fileCollection : false,
13918     allowBlank : true,
13919     
13920     getAutoCreate : function()
13921     {
13922         
13923         var cfg =  {
13924             cls :'form-group' ,
13925             cn : [
13926                
13927                 {
13928                     tag: 'label',
13929                    //cls : 'input-group-addon',
13930                     html : this.fieldLabel
13931
13932                 },
13933
13934                 {
13935                     tag: 'input',
13936                     type : 'hidden',
13937                     name : this.name,
13938                     value : this.value,
13939                     cls : 'd-none  form-control'
13940                 },
13941                 
13942                 {
13943                     tag: 'input',
13944                     multiple : 'multiple',
13945                     type : 'file',
13946                     cls : 'd-none  roo-card-upload-selector'
13947                 },
13948                 
13949                 {
13950                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13951                 },
13952                 {
13953                     cls : 'card-columns roo-card-uploader-container'
13954                 }
13955
13956             ]
13957         };
13958            
13959          
13960         return cfg;
13961     },
13962     
13963     getChildContainer : function() /// what children are added to.
13964     {
13965         return this.containerEl;
13966     },
13967    
13968     getButtonContainer : function() /// what children are added to.
13969     {
13970         return this.el.select(".roo-card-uploader-button-container").first();
13971     },
13972    
13973     initEvents : function()
13974     {
13975         
13976         Roo.bootstrap.Input.prototype.initEvents.call(this);
13977         
13978         var t = this;
13979         this.addxtype({
13980             xns: Roo.bootstrap,
13981
13982             xtype : 'Button',
13983             container_method : 'getButtonContainer' ,            
13984             html :  this.html, // fix changable?
13985             cls : 'w-100 ',
13986             listeners : {
13987                 'click' : function(btn, e) {
13988                     t.onClick(e);
13989                 }
13990             }
13991         });
13992         
13993         
13994         
13995         
13996         this.urlAPI = (window.createObjectURL && window) || 
13997                                 (window.URL && URL.revokeObjectURL && URL) || 
13998                                 (window.webkitURL && webkitURL);
13999                         
14000          
14001          
14002          
14003         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14004         
14005         this.selectorEl.on('change', this.onFileSelected, this);
14006         if (this.images) {
14007             var t = this;
14008             this.images.forEach(function(img) {
14009                 t.addCard(img)
14010             });
14011             this.images = false;
14012         }
14013         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14014          
14015        
14016     },
14017     
14018    
14019     onClick : function(e)
14020     {
14021         e.preventDefault();
14022          
14023         this.selectorEl.dom.click();
14024          
14025     },
14026     
14027     onFileSelected : function(e)
14028     {
14029         e.preventDefault();
14030         
14031         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14032             return;
14033         }
14034         
14035         Roo.each(this.selectorEl.dom.files, function(file){    
14036             this.addFile(file);
14037         }, this);
14038          
14039     },
14040     
14041       
14042     
14043       
14044     
14045     addFile : function(file)
14046     {
14047            
14048         if(typeof(file) === 'string'){
14049             throw "Add file by name?"; // should not happen
14050             return;
14051         }
14052         
14053         if(!file || !this.urlAPI){
14054             return;
14055         }
14056         
14057         // file;
14058         // file.type;
14059         
14060         var _this = this;
14061         
14062         
14063         var url = _this.urlAPI.createObjectURL( file);
14064            
14065         this.addCard({
14066             id : Roo.bootstrap.CardUploader.ID--,
14067             is_uploaded : false,
14068             src : url,
14069             srcfile : file,
14070             title : file.name,
14071             mimetype : file.type,
14072             preview : false,
14073             is_deleted : 0
14074         });
14075         
14076     },
14077     
14078     /**
14079      * addCard - add an Attachment to the uploader
14080      * @param data - the data about the image to upload
14081      *
14082      * {
14083           id : 123
14084           title : "Title of file",
14085           is_uploaded : false,
14086           src : "http://.....",
14087           srcfile : { the File upload object },
14088           mimetype : file.type,
14089           preview : false,
14090           is_deleted : 0
14091           .. any other data...
14092         }
14093      *
14094      * 
14095     */
14096     
14097     addCard : function (data)
14098     {
14099         // hidden input element?
14100         // if the file is not an image...
14101         //then we need to use something other that and header_image
14102         var t = this;
14103         //   remove.....
14104         var footer = [
14105             {
14106                 xns : Roo.bootstrap,
14107                 xtype : 'CardFooter',
14108                  items: [
14109                     {
14110                         xns : Roo.bootstrap,
14111                         xtype : 'Element',
14112                         cls : 'd-flex',
14113                         items : [
14114                             
14115                             {
14116                                 xns : Roo.bootstrap,
14117                                 xtype : 'Button',
14118                                 html : String.format("<small>{0}</small>", data.title),
14119                                 cls : 'col-10 text-left',
14120                                 size: 'sm',
14121                                 weight: 'link',
14122                                 fa : 'download',
14123                                 listeners : {
14124                                     click : function() {
14125                                      
14126                                         t.fireEvent( "download", t, data );
14127                                     }
14128                                 }
14129                             },
14130                           
14131                             {
14132                                 xns : Roo.bootstrap,
14133                                 xtype : 'Button',
14134                                 style: 'max-height: 28px; ',
14135                                 size : 'sm',
14136                                 weight: 'danger',
14137                                 cls : 'col-2',
14138                                 fa : 'times',
14139                                 listeners : {
14140                                     click : function() {
14141                                         t.removeCard(data.id)
14142                                     }
14143                                 }
14144                             }
14145                         ]
14146                     }
14147                     
14148                 ] 
14149             }
14150             
14151         ];
14152         
14153         var cn = this.addxtype(
14154             {
14155                  
14156                 xns : Roo.bootstrap,
14157                 xtype : 'Card',
14158                 closeable : true,
14159                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14160                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14161                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14162                 data : data,
14163                 html : false,
14164                  
14165                 items : footer,
14166                 initEvents : function() {
14167                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14168                     var card = this;
14169                     this.imgEl = this.el.select('.card-img-top').first();
14170                     if (this.imgEl) {
14171                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14172                         this.imgEl.set({ 'pointer' : 'cursor' });
14173                                   
14174                     }
14175                     this.getCardFooter().addClass('p-1');
14176                     
14177                   
14178                 }
14179                 
14180             }
14181         );
14182         // dont' really need ot update items.
14183         // this.items.push(cn);
14184         this.fileCollection.add(cn);
14185         
14186         if (!data.srcfile) {
14187             this.updateInput();
14188             return;
14189         }
14190             
14191         var _t = this;
14192         var reader = new FileReader();
14193         reader.addEventListener("load", function() {  
14194             data.srcdata =  reader.result;
14195             _t.updateInput();
14196         });
14197         reader.readAsDataURL(data.srcfile);
14198         
14199         
14200         
14201     },
14202     removeCard : function(id)
14203     {
14204         
14205         var card  = this.fileCollection.get(id);
14206         card.data.is_deleted = 1;
14207         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14208         //this.fileCollection.remove(card);
14209         //this.items = this.items.filter(function(e) { return e != card });
14210         // dont' really need ot update items.
14211         card.el.dom.parentNode.removeChild(card.el.dom);
14212         this.updateInput();
14213
14214         
14215     },
14216     reset: function()
14217     {
14218         this.fileCollection.each(function(card) {
14219             if (card.el.dom && card.el.dom.parentNode) {
14220                 card.el.dom.parentNode.removeChild(card.el.dom);
14221             }
14222         });
14223         this.fileCollection.clear();
14224         this.updateInput();
14225     },
14226     
14227     updateInput : function()
14228     {
14229          var data = [];
14230         this.fileCollection.each(function(e) {
14231             data.push(e.data);
14232             
14233         });
14234         this.inputEl().dom.value = JSON.stringify(data);
14235         
14236         
14237         
14238     }
14239     
14240     
14241 });
14242
14243
14244 Roo.bootstrap.CardUploader.ID = -1;/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255
14256 /**
14257  * @class Roo.data.SortTypes
14258  * @singleton
14259  * Defines the default sorting (casting?) comparison functions used when sorting data.
14260  */
14261 Roo.data.SortTypes = {
14262     /**
14263      * Default sort that does nothing
14264      * @param {Mixed} s The value being converted
14265      * @return {Mixed} The comparison value
14266      */
14267     none : function(s){
14268         return s;
14269     },
14270     
14271     /**
14272      * The regular expression used to strip tags
14273      * @type {RegExp}
14274      * @property
14275      */
14276     stripTagsRE : /<\/?[^>]+>/gi,
14277     
14278     /**
14279      * Strips all HTML tags to sort on text only
14280      * @param {Mixed} s The value being converted
14281      * @return {String} The comparison value
14282      */
14283     asText : function(s){
14284         return String(s).replace(this.stripTagsRE, "");
14285     },
14286     
14287     /**
14288      * Strips all HTML tags to sort on text only - Case insensitive
14289      * @param {Mixed} s The value being converted
14290      * @return {String} The comparison value
14291      */
14292     asUCText : function(s){
14293         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14294     },
14295     
14296     /**
14297      * Case insensitive string
14298      * @param {Mixed} s The value being converted
14299      * @return {String} The comparison value
14300      */
14301     asUCString : function(s) {
14302         return String(s).toUpperCase();
14303     },
14304     
14305     /**
14306      * Date sorting
14307      * @param {Mixed} s The value being converted
14308      * @return {Number} The comparison value
14309      */
14310     asDate : function(s) {
14311         if(!s){
14312             return 0;
14313         }
14314         if(s instanceof Date){
14315             return s.getTime();
14316         }
14317         return Date.parse(String(s));
14318     },
14319     
14320     /**
14321      * Float sorting
14322      * @param {Mixed} s The value being converted
14323      * @return {Float} The comparison value
14324      */
14325     asFloat : function(s) {
14326         var val = parseFloat(String(s).replace(/,/g, ""));
14327         if(isNaN(val)) {
14328             val = 0;
14329         }
14330         return val;
14331     },
14332     
14333     /**
14334      * Integer sorting
14335      * @param {Mixed} s The value being converted
14336      * @return {Number} The comparison value
14337      */
14338     asInt : function(s) {
14339         var val = parseInt(String(s).replace(/,/g, ""));
14340         if(isNaN(val)) {
14341             val = 0;
14342         }
14343         return val;
14344     }
14345 };/*
14346  * Based on:
14347  * Ext JS Library 1.1.1
14348  * Copyright(c) 2006-2007, Ext JS, LLC.
14349  *
14350  * Originally Released Under LGPL - original licence link has changed is not relivant.
14351  *
14352  * Fork - LGPL
14353  * <script type="text/javascript">
14354  */
14355
14356 /**
14357 * @class Roo.data.Record
14358  * Instances of this class encapsulate both record <em>definition</em> information, and record
14359  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14360  * to access Records cached in an {@link Roo.data.Store} object.<br>
14361  * <p>
14362  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14363  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14364  * objects.<br>
14365  * <p>
14366  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14367  * @constructor
14368  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14369  * {@link #create}. The parameters are the same.
14370  * @param {Array} data An associative Array of data values keyed by the field name.
14371  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14372  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14373  * not specified an integer id is generated.
14374  */
14375 Roo.data.Record = function(data, id){
14376     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14377     this.data = data;
14378 };
14379
14380 /**
14381  * Generate a constructor for a specific record layout.
14382  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14383  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14384  * Each field definition object may contain the following properties: <ul>
14385  * <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,
14386  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14387  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14388  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14389  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14390  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14391  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14392  * this may be omitted.</p></li>
14393  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14394  * <ul><li>auto (Default, implies no conversion)</li>
14395  * <li>string</li>
14396  * <li>int</li>
14397  * <li>float</li>
14398  * <li>boolean</li>
14399  * <li>date</li></ul></p></li>
14400  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14401  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14402  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14403  * by the Reader into an object that will be stored in the Record. It is passed the
14404  * following parameters:<ul>
14405  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14406  * </ul></p></li>
14407  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14408  * </ul>
14409  * <br>usage:<br><pre><code>
14410 var TopicRecord = Roo.data.Record.create(
14411     {name: 'title', mapping: 'topic_title'},
14412     {name: 'author', mapping: 'username'},
14413     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14414     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14415     {name: 'lastPoster', mapping: 'user2'},
14416     {name: 'excerpt', mapping: 'post_text'}
14417 );
14418
14419 var myNewRecord = new TopicRecord({
14420     title: 'Do my job please',
14421     author: 'noobie',
14422     totalPosts: 1,
14423     lastPost: new Date(),
14424     lastPoster: 'Animal',
14425     excerpt: 'No way dude!'
14426 });
14427 myStore.add(myNewRecord);
14428 </code></pre>
14429  * @method create
14430  * @static
14431  */
14432 Roo.data.Record.create = function(o){
14433     var f = function(){
14434         f.superclass.constructor.apply(this, arguments);
14435     };
14436     Roo.extend(f, Roo.data.Record);
14437     var p = f.prototype;
14438     p.fields = new Roo.util.MixedCollection(false, function(field){
14439         return field.name;
14440     });
14441     for(var i = 0, len = o.length; i < len; i++){
14442         p.fields.add(new Roo.data.Field(o[i]));
14443     }
14444     f.getField = function(name){
14445         return p.fields.get(name);  
14446     };
14447     return f;
14448 };
14449
14450 Roo.data.Record.AUTO_ID = 1000;
14451 Roo.data.Record.EDIT = 'edit';
14452 Roo.data.Record.REJECT = 'reject';
14453 Roo.data.Record.COMMIT = 'commit';
14454
14455 Roo.data.Record.prototype = {
14456     /**
14457      * Readonly flag - true if this record has been modified.
14458      * @type Boolean
14459      */
14460     dirty : false,
14461     editing : false,
14462     error: null,
14463     modified: null,
14464
14465     // private
14466     join : function(store){
14467         this.store = store;
14468     },
14469
14470     /**
14471      * Set the named field to the specified value.
14472      * @param {String} name The name of the field to set.
14473      * @param {Object} value The value to set the field to.
14474      */
14475     set : function(name, value){
14476         if(this.data[name] == value){
14477             return;
14478         }
14479         this.dirty = true;
14480         if(!this.modified){
14481             this.modified = {};
14482         }
14483         if(typeof this.modified[name] == 'undefined'){
14484             this.modified[name] = this.data[name];
14485         }
14486         this.data[name] = value;
14487         if(!this.editing && this.store){
14488             this.store.afterEdit(this);
14489         }       
14490     },
14491
14492     /**
14493      * Get the value of the named field.
14494      * @param {String} name The name of the field to get the value of.
14495      * @return {Object} The value of the field.
14496      */
14497     get : function(name){
14498         return this.data[name]; 
14499     },
14500
14501     // private
14502     beginEdit : function(){
14503         this.editing = true;
14504         this.modified = {}; 
14505     },
14506
14507     // private
14508     cancelEdit : function(){
14509         this.editing = false;
14510         delete this.modified;
14511     },
14512
14513     // private
14514     endEdit : function(){
14515         this.editing = false;
14516         if(this.dirty && this.store){
14517             this.store.afterEdit(this);
14518         }
14519     },
14520
14521     /**
14522      * Usually called by the {@link Roo.data.Store} which owns the Record.
14523      * Rejects all changes made to the Record since either creation, or the last commit operation.
14524      * Modified fields are reverted to their original values.
14525      * <p>
14526      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14527      * of reject operations.
14528      */
14529     reject : function(){
14530         var m = this.modified;
14531         for(var n in m){
14532             if(typeof m[n] != "function"){
14533                 this.data[n] = m[n];
14534             }
14535         }
14536         this.dirty = false;
14537         delete this.modified;
14538         this.editing = false;
14539         if(this.store){
14540             this.store.afterReject(this);
14541         }
14542     },
14543
14544     /**
14545      * Usually called by the {@link Roo.data.Store} which owns the Record.
14546      * Commits all changes made to the Record since either creation, or the last commit operation.
14547      * <p>
14548      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14549      * of commit operations.
14550      */
14551     commit : function(){
14552         this.dirty = false;
14553         delete this.modified;
14554         this.editing = false;
14555         if(this.store){
14556             this.store.afterCommit(this);
14557         }
14558     },
14559
14560     // private
14561     hasError : function(){
14562         return this.error != null;
14563     },
14564
14565     // private
14566     clearError : function(){
14567         this.error = null;
14568     },
14569
14570     /**
14571      * Creates a copy of this record.
14572      * @param {String} id (optional) A new record id if you don't want to use this record's id
14573      * @return {Record}
14574      */
14575     copy : function(newId) {
14576         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14577     }
14578 };/*
14579  * Based on:
14580  * Ext JS Library 1.1.1
14581  * Copyright(c) 2006-2007, Ext JS, LLC.
14582  *
14583  * Originally Released Under LGPL - original licence link has changed is not relivant.
14584  *
14585  * Fork - LGPL
14586  * <script type="text/javascript">
14587  */
14588
14589
14590
14591 /**
14592  * @class Roo.data.Store
14593  * @extends Roo.util.Observable
14594  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14595  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14596  * <p>
14597  * 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
14598  * has no knowledge of the format of the data returned by the Proxy.<br>
14599  * <p>
14600  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14601  * instances from the data object. These records are cached and made available through accessor functions.
14602  * @constructor
14603  * Creates a new Store.
14604  * @param {Object} config A config object containing the objects needed for the Store to access data,
14605  * and read the data into Records.
14606  */
14607 Roo.data.Store = function(config){
14608     this.data = new Roo.util.MixedCollection(false);
14609     this.data.getKey = function(o){
14610         return o.id;
14611     };
14612     this.baseParams = {};
14613     // private
14614     this.paramNames = {
14615         "start" : "start",
14616         "limit" : "limit",
14617         "sort" : "sort",
14618         "dir" : "dir",
14619         "multisort" : "_multisort"
14620     };
14621
14622     if(config && config.data){
14623         this.inlineData = config.data;
14624         delete config.data;
14625     }
14626
14627     Roo.apply(this, config);
14628     
14629     if(this.reader){ // reader passed
14630         this.reader = Roo.factory(this.reader, Roo.data);
14631         this.reader.xmodule = this.xmodule || false;
14632         if(!this.recordType){
14633             this.recordType = this.reader.recordType;
14634         }
14635         if(this.reader.onMetaChange){
14636             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14637         }
14638     }
14639
14640     if(this.recordType){
14641         this.fields = this.recordType.prototype.fields;
14642     }
14643     this.modified = [];
14644
14645     this.addEvents({
14646         /**
14647          * @event datachanged
14648          * Fires when the data cache has changed, and a widget which is using this Store
14649          * as a Record cache should refresh its view.
14650          * @param {Store} this
14651          */
14652         datachanged : true,
14653         /**
14654          * @event metachange
14655          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14656          * @param {Store} this
14657          * @param {Object} meta The JSON metadata
14658          */
14659         metachange : true,
14660         /**
14661          * @event add
14662          * Fires when Records have been added to the Store
14663          * @param {Store} this
14664          * @param {Roo.data.Record[]} records The array of Records added
14665          * @param {Number} index The index at which the record(s) were added
14666          */
14667         add : true,
14668         /**
14669          * @event remove
14670          * Fires when a Record has been removed from the Store
14671          * @param {Store} this
14672          * @param {Roo.data.Record} record The Record that was removed
14673          * @param {Number} index The index at which the record was removed
14674          */
14675         remove : true,
14676         /**
14677          * @event update
14678          * Fires when a Record has been updated
14679          * @param {Store} this
14680          * @param {Roo.data.Record} record The Record that was updated
14681          * @param {String} operation The update operation being performed.  Value may be one of:
14682          * <pre><code>
14683  Roo.data.Record.EDIT
14684  Roo.data.Record.REJECT
14685  Roo.data.Record.COMMIT
14686          * </code></pre>
14687          */
14688         update : true,
14689         /**
14690          * @event clear
14691          * Fires when the data cache has been cleared.
14692          * @param {Store} this
14693          */
14694         clear : true,
14695         /**
14696          * @event beforeload
14697          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14698          * the load action will be canceled.
14699          * @param {Store} this
14700          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14701          */
14702         beforeload : true,
14703         /**
14704          * @event beforeloadadd
14705          * Fires after a new set of Records has been loaded.
14706          * @param {Store} this
14707          * @param {Roo.data.Record[]} records The Records that were loaded
14708          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14709          */
14710         beforeloadadd : true,
14711         /**
14712          * @event load
14713          * Fires after a new set of Records has been loaded, before they are added to the store.
14714          * @param {Store} this
14715          * @param {Roo.data.Record[]} records The Records that were loaded
14716          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717          * @params {Object} return from reader
14718          */
14719         load : true,
14720         /**
14721          * @event loadexception
14722          * Fires if an exception occurs in the Proxy during loading.
14723          * Called with the signature of the Proxy's "loadexception" event.
14724          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14725          * 
14726          * @param {Proxy} 
14727          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14728          * @param {Object} load options 
14729          * @param {Object} jsonData from your request (normally this contains the Exception)
14730          */
14731         loadexception : true
14732     });
14733     
14734     if(this.proxy){
14735         this.proxy = Roo.factory(this.proxy, Roo.data);
14736         this.proxy.xmodule = this.xmodule || false;
14737         this.relayEvents(this.proxy,  ["loadexception"]);
14738     }
14739     this.sortToggle = {};
14740     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14741
14742     Roo.data.Store.superclass.constructor.call(this);
14743
14744     if(this.inlineData){
14745         this.loadData(this.inlineData);
14746         delete this.inlineData;
14747     }
14748 };
14749
14750 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14751      /**
14752     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14753     * without a remote query - used by combo/forms at present.
14754     */
14755     
14756     /**
14757     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14758     */
14759     /**
14760     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14761     */
14762     /**
14763     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14764     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14765     */
14766     /**
14767     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14768     * on any HTTP request
14769     */
14770     /**
14771     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14772     */
14773     /**
14774     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14775     */
14776     multiSort: false,
14777     /**
14778     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14779     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14780     */
14781     remoteSort : false,
14782
14783     /**
14784     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14785      * loaded or when a record is removed. (defaults to false).
14786     */
14787     pruneModifiedRecords : false,
14788
14789     // private
14790     lastOptions : null,
14791
14792     /**
14793      * Add Records to the Store and fires the add event.
14794      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14795      */
14796     add : function(records){
14797         records = [].concat(records);
14798         for(var i = 0, len = records.length; i < len; i++){
14799             records[i].join(this);
14800         }
14801         var index = this.data.length;
14802         this.data.addAll(records);
14803         this.fireEvent("add", this, records, index);
14804     },
14805
14806     /**
14807      * Remove a Record from the Store and fires the remove event.
14808      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14809      */
14810     remove : function(record){
14811         var index = this.data.indexOf(record);
14812         this.data.removeAt(index);
14813  
14814         if(this.pruneModifiedRecords){
14815             this.modified.remove(record);
14816         }
14817         this.fireEvent("remove", this, record, index);
14818     },
14819
14820     /**
14821      * Remove all Records from the Store and fires the clear event.
14822      */
14823     removeAll : function(){
14824         this.data.clear();
14825         if(this.pruneModifiedRecords){
14826             this.modified = [];
14827         }
14828         this.fireEvent("clear", this);
14829     },
14830
14831     /**
14832      * Inserts Records to the Store at the given index and fires the add event.
14833      * @param {Number} index The start index at which to insert the passed Records.
14834      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14835      */
14836     insert : function(index, records){
14837         records = [].concat(records);
14838         for(var i = 0, len = records.length; i < len; i++){
14839             this.data.insert(index, records[i]);
14840             records[i].join(this);
14841         }
14842         this.fireEvent("add", this, records, index);
14843     },
14844
14845     /**
14846      * Get the index within the cache of the passed Record.
14847      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14848      * @return {Number} The index of the passed Record. Returns -1 if not found.
14849      */
14850     indexOf : function(record){
14851         return this.data.indexOf(record);
14852     },
14853
14854     /**
14855      * Get the index within the cache of the Record with the passed id.
14856      * @param {String} id The id of the Record to find.
14857      * @return {Number} The index of the Record. Returns -1 if not found.
14858      */
14859     indexOfId : function(id){
14860         return this.data.indexOfKey(id);
14861     },
14862
14863     /**
14864      * Get the Record with the specified id.
14865      * @param {String} id The id of the Record to find.
14866      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14867      */
14868     getById : function(id){
14869         return this.data.key(id);
14870     },
14871
14872     /**
14873      * Get the Record at the specified index.
14874      * @param {Number} index The index of the Record to find.
14875      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14876      */
14877     getAt : function(index){
14878         return this.data.itemAt(index);
14879     },
14880
14881     /**
14882      * Returns a range of Records between specified indices.
14883      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14884      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14885      * @return {Roo.data.Record[]} An array of Records
14886      */
14887     getRange : function(start, end){
14888         return this.data.getRange(start, end);
14889     },
14890
14891     // private
14892     storeOptions : function(o){
14893         o = Roo.apply({}, o);
14894         delete o.callback;
14895         delete o.scope;
14896         this.lastOptions = o;
14897     },
14898
14899     /**
14900      * Loads the Record cache from the configured Proxy using the configured Reader.
14901      * <p>
14902      * If using remote paging, then the first load call must specify the <em>start</em>
14903      * and <em>limit</em> properties in the options.params property to establish the initial
14904      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14905      * <p>
14906      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14907      * and this call will return before the new data has been loaded. Perform any post-processing
14908      * in a callback function, or in a "load" event handler.</strong>
14909      * <p>
14910      * @param {Object} options An object containing properties which control loading options:<ul>
14911      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14912      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14913      * passed the following arguments:<ul>
14914      * <li>r : Roo.data.Record[]</li>
14915      * <li>options: Options object from the load call</li>
14916      * <li>success: Boolean success indicator</li></ul></li>
14917      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14918      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14919      * </ul>
14920      */
14921     load : function(options){
14922         options = options || {};
14923         if(this.fireEvent("beforeload", this, options) !== false){
14924             this.storeOptions(options);
14925             var p = Roo.apply(options.params || {}, this.baseParams);
14926             // if meta was not loaded from remote source.. try requesting it.
14927             if (!this.reader.metaFromRemote) {
14928                 p._requestMeta = 1;
14929             }
14930             if(this.sortInfo && this.remoteSort){
14931                 var pn = this.paramNames;
14932                 p[pn["sort"]] = this.sortInfo.field;
14933                 p[pn["dir"]] = this.sortInfo.direction;
14934             }
14935             if (this.multiSort) {
14936                 var pn = this.paramNames;
14937                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14938             }
14939             
14940             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14941         }
14942     },
14943
14944     /**
14945      * Reloads the Record cache from the configured Proxy using the configured Reader and
14946      * the options from the last load operation performed.
14947      * @param {Object} options (optional) An object containing properties which may override the options
14948      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14949      * the most recently used options are reused).
14950      */
14951     reload : function(options){
14952         this.load(Roo.applyIf(options||{}, this.lastOptions));
14953     },
14954
14955     // private
14956     // Called as a callback by the Reader during a load operation.
14957     loadRecords : function(o, options, success){
14958         if(!o || success === false){
14959             if(success !== false){
14960                 this.fireEvent("load", this, [], options, o);
14961             }
14962             if(options.callback){
14963                 options.callback.call(options.scope || this, [], options, false);
14964             }
14965             return;
14966         }
14967         // if data returned failure - throw an exception.
14968         if (o.success === false) {
14969             // show a message if no listener is registered.
14970             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14971                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14972             }
14973             // loadmask wil be hooked into this..
14974             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14975             return;
14976         }
14977         var r = o.records, t = o.totalRecords || r.length;
14978         
14979         this.fireEvent("beforeloadadd", this, r, options, o);
14980         
14981         if(!options || options.add !== true){
14982             if(this.pruneModifiedRecords){
14983                 this.modified = [];
14984             }
14985             for(var i = 0, len = r.length; i < len; i++){
14986                 r[i].join(this);
14987             }
14988             if(this.snapshot){
14989                 this.data = this.snapshot;
14990                 delete this.snapshot;
14991             }
14992             this.data.clear();
14993             this.data.addAll(r);
14994             this.totalLength = t;
14995             this.applySort();
14996             this.fireEvent("datachanged", this);
14997         }else{
14998             this.totalLength = Math.max(t, this.data.length+r.length);
14999             this.add(r);
15000         }
15001         
15002         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15003                 
15004             var e = new Roo.data.Record({});
15005
15006             e.set(this.parent.displayField, this.parent.emptyTitle);
15007             e.set(this.parent.valueField, '');
15008
15009             this.insert(0, e);
15010         }
15011             
15012         this.fireEvent("load", this, r, options, o);
15013         if(options.callback){
15014             options.callback.call(options.scope || this, r, options, true);
15015         }
15016     },
15017
15018
15019     /**
15020      * Loads data from a passed data block. A Reader which understands the format of the data
15021      * must have been configured in the constructor.
15022      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15023      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15024      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15025      */
15026     loadData : function(o, append){
15027         var r = this.reader.readRecords(o);
15028         this.loadRecords(r, {add: append}, true);
15029     },
15030     
15031      /**
15032      * using 'cn' the nested child reader read the child array into it's child stores.
15033      * @param {Object} rec The record with a 'children array
15034      */
15035     loadDataFromChildren : function(rec)
15036     {
15037         this.loadData(this.reader.toLoadData(rec));
15038     },
15039     
15040
15041     /**
15042      * Gets the number of cached records.
15043      * <p>
15044      * <em>If using paging, this may not be the total size of the dataset. If the data object
15045      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15046      * the data set size</em>
15047      */
15048     getCount : function(){
15049         return this.data.length || 0;
15050     },
15051
15052     /**
15053      * Gets the total number of records in the dataset as returned by the server.
15054      * <p>
15055      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15056      * the dataset size</em>
15057      */
15058     getTotalCount : function(){
15059         return this.totalLength || 0;
15060     },
15061
15062     /**
15063      * Returns the sort state of the Store as an object with two properties:
15064      * <pre><code>
15065  field {String} The name of the field by which the Records are sorted
15066  direction {String} The sort order, "ASC" or "DESC"
15067      * </code></pre>
15068      */
15069     getSortState : function(){
15070         return this.sortInfo;
15071     },
15072
15073     // private
15074     applySort : function(){
15075         if(this.sortInfo && !this.remoteSort){
15076             var s = this.sortInfo, f = s.field;
15077             var st = this.fields.get(f).sortType;
15078             var fn = function(r1, r2){
15079                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15080                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15081             };
15082             this.data.sort(s.direction, fn);
15083             if(this.snapshot && this.snapshot != this.data){
15084                 this.snapshot.sort(s.direction, fn);
15085             }
15086         }
15087     },
15088
15089     /**
15090      * Sets the default sort column and order to be used by the next load operation.
15091      * @param {String} fieldName The name of the field to sort by.
15092      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15093      */
15094     setDefaultSort : function(field, dir){
15095         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15096     },
15097
15098     /**
15099      * Sort the Records.
15100      * If remote sorting is used, the sort is performed on the server, and the cache is
15101      * reloaded. If local sorting is used, the cache is sorted internally.
15102      * @param {String} fieldName The name of the field to sort by.
15103      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15104      */
15105     sort : function(fieldName, dir){
15106         var f = this.fields.get(fieldName);
15107         if(!dir){
15108             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15109             
15110             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15111                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15112             }else{
15113                 dir = f.sortDir;
15114             }
15115         }
15116         this.sortToggle[f.name] = dir;
15117         this.sortInfo = {field: f.name, direction: dir};
15118         if(!this.remoteSort){
15119             this.applySort();
15120             this.fireEvent("datachanged", this);
15121         }else{
15122             this.load(this.lastOptions);
15123         }
15124     },
15125
15126     /**
15127      * Calls the specified function for each of the Records in the cache.
15128      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15129      * Returning <em>false</em> aborts and exits the iteration.
15130      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15131      */
15132     each : function(fn, scope){
15133         this.data.each(fn, scope);
15134     },
15135
15136     /**
15137      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15138      * (e.g., during paging).
15139      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15140      */
15141     getModifiedRecords : function(){
15142         return this.modified;
15143     },
15144
15145     // private
15146     createFilterFn : function(property, value, anyMatch){
15147         if(!value.exec){ // not a regex
15148             value = String(value);
15149             if(value.length == 0){
15150                 return false;
15151             }
15152             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15153         }
15154         return function(r){
15155             return value.test(r.data[property]);
15156         };
15157     },
15158
15159     /**
15160      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15161      * @param {String} property A field on your records
15162      * @param {Number} start The record index to start at (defaults to 0)
15163      * @param {Number} end The last record index to include (defaults to length - 1)
15164      * @return {Number} The sum
15165      */
15166     sum : function(property, start, end){
15167         var rs = this.data.items, v = 0;
15168         start = start || 0;
15169         end = (end || end === 0) ? end : rs.length-1;
15170
15171         for(var i = start; i <= end; i++){
15172             v += (rs[i].data[property] || 0);
15173         }
15174         return v;
15175     },
15176
15177     /**
15178      * Filter the records by a specified property.
15179      * @param {String} field A field on your records
15180      * @param {String/RegExp} value Either a string that the field
15181      * should start with or a RegExp to test against the field
15182      * @param {Boolean} anyMatch True to match any part not just the beginning
15183      */
15184     filter : function(property, value, anyMatch){
15185         var fn = this.createFilterFn(property, value, anyMatch);
15186         return fn ? this.filterBy(fn) : this.clearFilter();
15187     },
15188
15189     /**
15190      * Filter by a function. The specified function will be called with each
15191      * record in this data source. If the function returns true the record is included,
15192      * otherwise it is filtered.
15193      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15194      * @param {Object} scope (optional) The scope of the function (defaults to this)
15195      */
15196     filterBy : function(fn, scope){
15197         this.snapshot = this.snapshot || this.data;
15198         this.data = this.queryBy(fn, scope||this);
15199         this.fireEvent("datachanged", this);
15200     },
15201
15202     /**
15203      * Query the records by a specified property.
15204      * @param {String} field A field on your records
15205      * @param {String/RegExp} value Either a string that the field
15206      * should start with or a RegExp to test against the field
15207      * @param {Boolean} anyMatch True to match any part not just the beginning
15208      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15209      */
15210     query : function(property, value, anyMatch){
15211         var fn = this.createFilterFn(property, value, anyMatch);
15212         return fn ? this.queryBy(fn) : this.data.clone();
15213     },
15214
15215     /**
15216      * Query by a function. The specified function will be called with each
15217      * record in this data source. If the function returns true the record is included
15218      * in the results.
15219      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15220      * @param {Object} scope (optional) The scope of the function (defaults to this)
15221       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15222      **/
15223     queryBy : function(fn, scope){
15224         var data = this.snapshot || this.data;
15225         return data.filterBy(fn, scope||this);
15226     },
15227
15228     /**
15229      * Collects unique values for a particular dataIndex from this store.
15230      * @param {String} dataIndex The property to collect
15231      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15232      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15233      * @return {Array} An array of the unique values
15234      **/
15235     collect : function(dataIndex, allowNull, bypassFilter){
15236         var d = (bypassFilter === true && this.snapshot) ?
15237                 this.snapshot.items : this.data.items;
15238         var v, sv, r = [], l = {};
15239         for(var i = 0, len = d.length; i < len; i++){
15240             v = d[i].data[dataIndex];
15241             sv = String(v);
15242             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15243                 l[sv] = true;
15244                 r[r.length] = v;
15245             }
15246         }
15247         return r;
15248     },
15249
15250     /**
15251      * Revert to a view of the Record cache with no filtering applied.
15252      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15253      */
15254     clearFilter : function(suppressEvent){
15255         if(this.snapshot && this.snapshot != this.data){
15256             this.data = this.snapshot;
15257             delete this.snapshot;
15258             if(suppressEvent !== true){
15259                 this.fireEvent("datachanged", this);
15260             }
15261         }
15262     },
15263
15264     // private
15265     afterEdit : function(record){
15266         if(this.modified.indexOf(record) == -1){
15267             this.modified.push(record);
15268         }
15269         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15270     },
15271     
15272     // private
15273     afterReject : function(record){
15274         this.modified.remove(record);
15275         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15276     },
15277
15278     // private
15279     afterCommit : function(record){
15280         this.modified.remove(record);
15281         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15282     },
15283
15284     /**
15285      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15286      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15287      */
15288     commitChanges : function(){
15289         var m = this.modified.slice(0);
15290         this.modified = [];
15291         for(var i = 0, len = m.length; i < len; i++){
15292             m[i].commit();
15293         }
15294     },
15295
15296     /**
15297      * Cancel outstanding changes on all changed records.
15298      */
15299     rejectChanges : function(){
15300         var m = this.modified.slice(0);
15301         this.modified = [];
15302         for(var i = 0, len = m.length; i < len; i++){
15303             m[i].reject();
15304         }
15305     },
15306
15307     onMetaChange : function(meta, rtype, o){
15308         this.recordType = rtype;
15309         this.fields = rtype.prototype.fields;
15310         delete this.snapshot;
15311         this.sortInfo = meta.sortInfo || this.sortInfo;
15312         this.modified = [];
15313         this.fireEvent('metachange', this, this.reader.meta);
15314     },
15315     
15316     moveIndex : function(data, type)
15317     {
15318         var index = this.indexOf(data);
15319         
15320         var newIndex = index + type;
15321         
15322         this.remove(data);
15323         
15324         this.insert(newIndex, data);
15325         
15326     }
15327 });/*
15328  * Based on:
15329  * Ext JS Library 1.1.1
15330  * Copyright(c) 2006-2007, Ext JS, LLC.
15331  *
15332  * Originally Released Under LGPL - original licence link has changed is not relivant.
15333  *
15334  * Fork - LGPL
15335  * <script type="text/javascript">
15336  */
15337
15338 /**
15339  * @class Roo.data.SimpleStore
15340  * @extends Roo.data.Store
15341  * Small helper class to make creating Stores from Array data easier.
15342  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15343  * @cfg {Array} fields An array of field definition objects, or field name strings.
15344  * @cfg {Object} an existing reader (eg. copied from another store)
15345  * @cfg {Array} data The multi-dimensional array of data
15346  * @constructor
15347  * @param {Object} config
15348  */
15349 Roo.data.SimpleStore = function(config)
15350 {
15351     Roo.data.SimpleStore.superclass.constructor.call(this, {
15352         isLocal : true,
15353         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15354                 id: config.id
15355             },
15356             Roo.data.Record.create(config.fields)
15357         ),
15358         proxy : new Roo.data.MemoryProxy(config.data)
15359     });
15360     this.load();
15361 };
15362 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15363  * Based on:
15364  * Ext JS Library 1.1.1
15365  * Copyright(c) 2006-2007, Ext JS, LLC.
15366  *
15367  * Originally Released Under LGPL - original licence link has changed is not relivant.
15368  *
15369  * Fork - LGPL
15370  * <script type="text/javascript">
15371  */
15372
15373 /**
15374 /**
15375  * @extends Roo.data.Store
15376  * @class Roo.data.JsonStore
15377  * Small helper class to make creating Stores for JSON data easier. <br/>
15378 <pre><code>
15379 var store = new Roo.data.JsonStore({
15380     url: 'get-images.php',
15381     root: 'images',
15382     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15383 });
15384 </code></pre>
15385  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15386  * JsonReader and HttpProxy (unless inline data is provided).</b>
15387  * @cfg {Array} fields An array of field definition objects, or field name strings.
15388  * @constructor
15389  * @param {Object} config
15390  */
15391 Roo.data.JsonStore = function(c){
15392     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15393         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15394         reader: new Roo.data.JsonReader(c, c.fields)
15395     }));
15396 };
15397 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408  
15409 Roo.data.Field = function(config){
15410     if(typeof config == "string"){
15411         config = {name: config};
15412     }
15413     Roo.apply(this, config);
15414     
15415     if(!this.type){
15416         this.type = "auto";
15417     }
15418     
15419     var st = Roo.data.SortTypes;
15420     // named sortTypes are supported, here we look them up
15421     if(typeof this.sortType == "string"){
15422         this.sortType = st[this.sortType];
15423     }
15424     
15425     // set default sortType for strings and dates
15426     if(!this.sortType){
15427         switch(this.type){
15428             case "string":
15429                 this.sortType = st.asUCString;
15430                 break;
15431             case "date":
15432                 this.sortType = st.asDate;
15433                 break;
15434             default:
15435                 this.sortType = st.none;
15436         }
15437     }
15438
15439     // define once
15440     var stripRe = /[\$,%]/g;
15441
15442     // prebuilt conversion function for this field, instead of
15443     // switching every time we're reading a value
15444     if(!this.convert){
15445         var cv, dateFormat = this.dateFormat;
15446         switch(this.type){
15447             case "":
15448             case "auto":
15449             case undefined:
15450                 cv = function(v){ return v; };
15451                 break;
15452             case "string":
15453                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15454                 break;
15455             case "int":
15456                 cv = function(v){
15457                     return v !== undefined && v !== null && v !== '' ?
15458                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15459                     };
15460                 break;
15461             case "float":
15462                 cv = function(v){
15463                     return v !== undefined && v !== null && v !== '' ?
15464                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15465                     };
15466                 break;
15467             case "bool":
15468             case "boolean":
15469                 cv = function(v){ return v === true || v === "true" || v == 1; };
15470                 break;
15471             case "date":
15472                 cv = function(v){
15473                     if(!v){
15474                         return '';
15475                     }
15476                     if(v instanceof Date){
15477                         return v;
15478                     }
15479                     if(dateFormat){
15480                         if(dateFormat == "timestamp"){
15481                             return new Date(v*1000);
15482                         }
15483                         return Date.parseDate(v, dateFormat);
15484                     }
15485                     var parsed = Date.parse(v);
15486                     return parsed ? new Date(parsed) : null;
15487                 };
15488              break;
15489             
15490         }
15491         this.convert = cv;
15492     }
15493 };
15494
15495 Roo.data.Field.prototype = {
15496     dateFormat: null,
15497     defaultValue: "",
15498     mapping: null,
15499     sortType : null,
15500     sortDir : "ASC"
15501 };/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511  
15512 // Base class for reading structured data from a data source.  This class is intended to be
15513 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15514
15515 /**
15516  * @class Roo.data.DataReader
15517  * Base class for reading structured data from a data source.  This class is intended to be
15518  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15519  */
15520
15521 Roo.data.DataReader = function(meta, recordType){
15522     
15523     this.meta = meta;
15524     
15525     this.recordType = recordType instanceof Array ? 
15526         Roo.data.Record.create(recordType) : recordType;
15527 };
15528
15529 Roo.data.DataReader.prototype = {
15530     
15531     
15532     readerType : 'Data',
15533      /**
15534      * Create an empty record
15535      * @param {Object} data (optional) - overlay some values
15536      * @return {Roo.data.Record} record created.
15537      */
15538     newRow :  function(d) {
15539         var da =  {};
15540         this.recordType.prototype.fields.each(function(c) {
15541             switch( c.type) {
15542                 case 'int' : da[c.name] = 0; break;
15543                 case 'date' : da[c.name] = new Date(); break;
15544                 case 'float' : da[c.name] = 0.0; break;
15545                 case 'boolean' : da[c.name] = false; break;
15546                 default : da[c.name] = ""; break;
15547             }
15548             
15549         });
15550         return new this.recordType(Roo.apply(da, d));
15551     }
15552     
15553     
15554 };/*
15555  * Based on:
15556  * Ext JS Library 1.1.1
15557  * Copyright(c) 2006-2007, Ext JS, LLC.
15558  *
15559  * Originally Released Under LGPL - original licence link has changed is not relivant.
15560  *
15561  * Fork - LGPL
15562  * <script type="text/javascript">
15563  */
15564
15565 /**
15566  * @class Roo.data.DataProxy
15567  * @extends Roo.data.Observable
15568  * This class is an abstract base class for implementations which provide retrieval of
15569  * unformatted data objects.<br>
15570  * <p>
15571  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15572  * (of the appropriate type which knows how to parse the data object) to provide a block of
15573  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15574  * <p>
15575  * Custom implementations must implement the load method as described in
15576  * {@link Roo.data.HttpProxy#load}.
15577  */
15578 Roo.data.DataProxy = function(){
15579     this.addEvents({
15580         /**
15581          * @event beforeload
15582          * Fires before a network request is made to retrieve a data object.
15583          * @param {Object} This DataProxy object.
15584          * @param {Object} params The params parameter to the load function.
15585          */
15586         beforeload : true,
15587         /**
15588          * @event load
15589          * Fires before the load method's callback is called.
15590          * @param {Object} This DataProxy object.
15591          * @param {Object} o The data object.
15592          * @param {Object} arg The callback argument object passed to the load function.
15593          */
15594         load : true,
15595         /**
15596          * @event loadexception
15597          * Fires if an Exception occurs during data retrieval.
15598          * @param {Object} This DataProxy object.
15599          * @param {Object} o The data object.
15600          * @param {Object} arg The callback argument object passed to the load function.
15601          * @param {Object} e The Exception.
15602          */
15603         loadexception : true
15604     });
15605     Roo.data.DataProxy.superclass.constructor.call(this);
15606 };
15607
15608 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15609
15610     /**
15611      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15612      */
15613 /*
15614  * Based on:
15615  * Ext JS Library 1.1.1
15616  * Copyright(c) 2006-2007, Ext JS, LLC.
15617  *
15618  * Originally Released Under LGPL - original licence link has changed is not relivant.
15619  *
15620  * Fork - LGPL
15621  * <script type="text/javascript">
15622  */
15623 /**
15624  * @class Roo.data.MemoryProxy
15625  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15626  * to the Reader when its load method is called.
15627  * @constructor
15628  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15629  */
15630 Roo.data.MemoryProxy = function(data){
15631     if (data.data) {
15632         data = data.data;
15633     }
15634     Roo.data.MemoryProxy.superclass.constructor.call(this);
15635     this.data = data;
15636 };
15637
15638 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15639     
15640     /**
15641      * Load data from the requested source (in this case an in-memory
15642      * data object passed to the constructor), read the data object into
15643      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15644      * process that block using the passed callback.
15645      * @param {Object} params This parameter is not used by the MemoryProxy class.
15646      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15647      * object into a block of Roo.data.Records.
15648      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15649      * The function must be passed <ul>
15650      * <li>The Record block object</li>
15651      * <li>The "arg" argument from the load function</li>
15652      * <li>A boolean success indicator</li>
15653      * </ul>
15654      * @param {Object} scope The scope in which to call the callback
15655      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15656      */
15657     load : function(params, reader, callback, scope, arg){
15658         params = params || {};
15659         var result;
15660         try {
15661             result = reader.readRecords(params.data ? params.data :this.data);
15662         }catch(e){
15663             this.fireEvent("loadexception", this, arg, null, e);
15664             callback.call(scope, null, arg, false);
15665             return;
15666         }
15667         callback.call(scope, result, arg, true);
15668     },
15669     
15670     // private
15671     update : function(params, records){
15672         
15673     }
15674 });/*
15675  * Based on:
15676  * Ext JS Library 1.1.1
15677  * Copyright(c) 2006-2007, Ext JS, LLC.
15678  *
15679  * Originally Released Under LGPL - original licence link has changed is not relivant.
15680  *
15681  * Fork - LGPL
15682  * <script type="text/javascript">
15683  */
15684 /**
15685  * @class Roo.data.HttpProxy
15686  * @extends Roo.data.DataProxy
15687  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15688  * configured to reference a certain URL.<br><br>
15689  * <p>
15690  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15691  * from which the running page was served.<br><br>
15692  * <p>
15693  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15694  * <p>
15695  * Be aware that to enable the browser to parse an XML document, the server must set
15696  * the Content-Type header in the HTTP response to "text/xml".
15697  * @constructor
15698  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15699  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15700  * will be used to make the request.
15701  */
15702 Roo.data.HttpProxy = function(conn){
15703     Roo.data.HttpProxy.superclass.constructor.call(this);
15704     // is conn a conn config or a real conn?
15705     this.conn = conn;
15706     this.useAjax = !conn || !conn.events;
15707   
15708 };
15709
15710 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15711     // thse are take from connection...
15712     
15713     /**
15714      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15715      */
15716     /**
15717      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15718      * extra parameters to each request made by this object. (defaults to undefined)
15719      */
15720     /**
15721      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15722      *  to each request made by this object. (defaults to undefined)
15723      */
15724     /**
15725      * @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)
15726      */
15727     /**
15728      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15729      */
15730      /**
15731      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15732      * @type Boolean
15733      */
15734   
15735
15736     /**
15737      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15738      * @type Boolean
15739      */
15740     /**
15741      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15742      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15743      * a finer-grained basis than the DataProxy events.
15744      */
15745     getConnection : function(){
15746         return this.useAjax ? Roo.Ajax : this.conn;
15747     },
15748
15749     /**
15750      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15751      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15752      * process that block using the passed callback.
15753      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15754      * for the request to the remote server.
15755      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15756      * object into a block of Roo.data.Records.
15757      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15758      * The function must be passed <ul>
15759      * <li>The Record block object</li>
15760      * <li>The "arg" argument from the load function</li>
15761      * <li>A boolean success indicator</li>
15762      * </ul>
15763      * @param {Object} scope The scope in which to call the callback
15764      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15765      */
15766     load : function(params, reader, callback, scope, arg){
15767         if(this.fireEvent("beforeload", this, params) !== false){
15768             var  o = {
15769                 params : params || {},
15770                 request: {
15771                     callback : callback,
15772                     scope : scope,
15773                     arg : arg
15774                 },
15775                 reader: reader,
15776                 callback : this.loadResponse,
15777                 scope: this
15778             };
15779             if(this.useAjax){
15780                 Roo.applyIf(o, this.conn);
15781                 if(this.activeRequest){
15782                     Roo.Ajax.abort(this.activeRequest);
15783                 }
15784                 this.activeRequest = Roo.Ajax.request(o);
15785             }else{
15786                 this.conn.request(o);
15787             }
15788         }else{
15789             callback.call(scope||this, null, arg, false);
15790         }
15791     },
15792
15793     // private
15794     loadResponse : function(o, success, response){
15795         delete this.activeRequest;
15796         if(!success){
15797             this.fireEvent("loadexception", this, o, response);
15798             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15799             return;
15800         }
15801         var result;
15802         try {
15803             result = o.reader.read(response);
15804         }catch(e){
15805             this.fireEvent("loadexception", this, o, response, e);
15806             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15807             return;
15808         }
15809         
15810         this.fireEvent("load", this, o, o.request.arg);
15811         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15812     },
15813
15814     // private
15815     update : function(dataSet){
15816
15817     },
15818
15819     // private
15820     updateResponse : function(dataSet){
15821
15822     }
15823 });/*
15824  * Based on:
15825  * Ext JS Library 1.1.1
15826  * Copyright(c) 2006-2007, Ext JS, LLC.
15827  *
15828  * Originally Released Under LGPL - original licence link has changed is not relivant.
15829  *
15830  * Fork - LGPL
15831  * <script type="text/javascript">
15832  */
15833
15834 /**
15835  * @class Roo.data.ScriptTagProxy
15836  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15837  * other than the originating domain of the running page.<br><br>
15838  * <p>
15839  * <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
15840  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15841  * <p>
15842  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15843  * source code that is used as the source inside a &lt;script> tag.<br><br>
15844  * <p>
15845  * In order for the browser to process the returned data, the server must wrap the data object
15846  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15847  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15848  * depending on whether the callback name was passed:
15849  * <p>
15850  * <pre><code>
15851 boolean scriptTag = false;
15852 String cb = request.getParameter("callback");
15853 if (cb != null) {
15854     scriptTag = true;
15855     response.setContentType("text/javascript");
15856 } else {
15857     response.setContentType("application/x-json");
15858 }
15859 Writer out = response.getWriter();
15860 if (scriptTag) {
15861     out.write(cb + "(");
15862 }
15863 out.print(dataBlock.toJsonString());
15864 if (scriptTag) {
15865     out.write(");");
15866 }
15867 </pre></code>
15868  *
15869  * @constructor
15870  * @param {Object} config A configuration object.
15871  */
15872 Roo.data.ScriptTagProxy = function(config){
15873     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15874     Roo.apply(this, config);
15875     this.head = document.getElementsByTagName("head")[0];
15876 };
15877
15878 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15879
15880 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15881     /**
15882      * @cfg {String} url The URL from which to request the data object.
15883      */
15884     /**
15885      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15886      */
15887     timeout : 30000,
15888     /**
15889      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15890      * the server the name of the callback function set up by the load call to process the returned data object.
15891      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15892      * javascript output which calls this named function passing the data object as its only parameter.
15893      */
15894     callbackParam : "callback",
15895     /**
15896      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15897      * name to the request.
15898      */
15899     nocache : true,
15900
15901     /**
15902      * Load data from the configured URL, read the data object into
15903      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15904      * process that block using the passed callback.
15905      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15906      * for the request to the remote server.
15907      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15908      * object into a block of Roo.data.Records.
15909      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15910      * The function must be passed <ul>
15911      * <li>The Record block object</li>
15912      * <li>The "arg" argument from the load function</li>
15913      * <li>A boolean success indicator</li>
15914      * </ul>
15915      * @param {Object} scope The scope in which to call the callback
15916      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15917      */
15918     load : function(params, reader, callback, scope, arg){
15919         if(this.fireEvent("beforeload", this, params) !== false){
15920
15921             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15922
15923             var url = this.url;
15924             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15925             if(this.nocache){
15926                 url += "&_dc=" + (new Date().getTime());
15927             }
15928             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15929             var trans = {
15930                 id : transId,
15931                 cb : "stcCallback"+transId,
15932                 scriptId : "stcScript"+transId,
15933                 params : params,
15934                 arg : arg,
15935                 url : url,
15936                 callback : callback,
15937                 scope : scope,
15938                 reader : reader
15939             };
15940             var conn = this;
15941
15942             window[trans.cb] = function(o){
15943                 conn.handleResponse(o, trans);
15944             };
15945
15946             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15947
15948             if(this.autoAbort !== false){
15949                 this.abort();
15950             }
15951
15952             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15953
15954             var script = document.createElement("script");
15955             script.setAttribute("src", url);
15956             script.setAttribute("type", "text/javascript");
15957             script.setAttribute("id", trans.scriptId);
15958             this.head.appendChild(script);
15959
15960             this.trans = trans;
15961         }else{
15962             callback.call(scope||this, null, arg, false);
15963         }
15964     },
15965
15966     // private
15967     isLoading : function(){
15968         return this.trans ? true : false;
15969     },
15970
15971     /**
15972      * Abort the current server request.
15973      */
15974     abort : function(){
15975         if(this.isLoading()){
15976             this.destroyTrans(this.trans);
15977         }
15978     },
15979
15980     // private
15981     destroyTrans : function(trans, isLoaded){
15982         this.head.removeChild(document.getElementById(trans.scriptId));
15983         clearTimeout(trans.timeoutId);
15984         if(isLoaded){
15985             window[trans.cb] = undefined;
15986             try{
15987                 delete window[trans.cb];
15988             }catch(e){}
15989         }else{
15990             // if hasn't been loaded, wait for load to remove it to prevent script error
15991             window[trans.cb] = function(){
15992                 window[trans.cb] = undefined;
15993                 try{
15994                     delete window[trans.cb];
15995                 }catch(e){}
15996             };
15997         }
15998     },
15999
16000     // private
16001     handleResponse : function(o, trans){
16002         this.trans = false;
16003         this.destroyTrans(trans, true);
16004         var result;
16005         try {
16006             result = trans.reader.readRecords(o);
16007         }catch(e){
16008             this.fireEvent("loadexception", this, o, trans.arg, e);
16009             trans.callback.call(trans.scope||window, null, trans.arg, false);
16010             return;
16011         }
16012         this.fireEvent("load", this, o, trans.arg);
16013         trans.callback.call(trans.scope||window, result, trans.arg, true);
16014     },
16015
16016     // private
16017     handleFailure : function(trans){
16018         this.trans = false;
16019         this.destroyTrans(trans, false);
16020         this.fireEvent("loadexception", this, null, trans.arg);
16021         trans.callback.call(trans.scope||window, null, trans.arg, false);
16022     }
16023 });/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033
16034 /**
16035  * @class Roo.data.JsonReader
16036  * @extends Roo.data.DataReader
16037  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16038  * based on mappings in a provided Roo.data.Record constructor.
16039  * 
16040  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16041  * in the reply previously. 
16042  * 
16043  * <p>
16044  * Example code:
16045  * <pre><code>
16046 var RecordDef = Roo.data.Record.create([
16047     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16048     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16049 ]);
16050 var myReader = new Roo.data.JsonReader({
16051     totalProperty: "results",    // The property which contains the total dataset size (optional)
16052     root: "rows",                // The property which contains an Array of row objects
16053     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16054 }, RecordDef);
16055 </code></pre>
16056  * <p>
16057  * This would consume a JSON file like this:
16058  * <pre><code>
16059 { 'results': 2, 'rows': [
16060     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16061     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16062 }
16063 </code></pre>
16064  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16065  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16066  * paged from the remote server.
16067  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16068  * @cfg {String} root name of the property which contains the Array of row objects.
16069  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16070  * @cfg {Array} fields Array of field definition objects
16071  * @constructor
16072  * Create a new JsonReader
16073  * @param {Object} meta Metadata configuration options
16074  * @param {Object} recordType Either an Array of field definition objects,
16075  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16076  */
16077 Roo.data.JsonReader = function(meta, recordType){
16078     
16079     meta = meta || {};
16080     // set some defaults:
16081     Roo.applyIf(meta, {
16082         totalProperty: 'total',
16083         successProperty : 'success',
16084         root : 'data',
16085         id : 'id'
16086     });
16087     
16088     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16089 };
16090 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16091     
16092     readerType : 'Json',
16093     
16094     /**
16095      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16096      * Used by Store query builder to append _requestMeta to params.
16097      * 
16098      */
16099     metaFromRemote : false,
16100     /**
16101      * This method is only used by a DataProxy which has retrieved data from a remote server.
16102      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16103      * @return {Object} data A data block which is used by an Roo.data.Store object as
16104      * a cache of Roo.data.Records.
16105      */
16106     read : function(response){
16107         var json = response.responseText;
16108        
16109         var o = /* eval:var:o */ eval("("+json+")");
16110         if(!o) {
16111             throw {message: "JsonReader.read: Json object not found"};
16112         }
16113         
16114         if(o.metaData){
16115             
16116             delete this.ef;
16117             this.metaFromRemote = true;
16118             this.meta = o.metaData;
16119             this.recordType = Roo.data.Record.create(o.metaData.fields);
16120             this.onMetaChange(this.meta, this.recordType, o);
16121         }
16122         return this.readRecords(o);
16123     },
16124
16125     // private function a store will implement
16126     onMetaChange : function(meta, recordType, o){
16127
16128     },
16129
16130     /**
16131          * @ignore
16132          */
16133     simpleAccess: function(obj, subsc) {
16134         return obj[subsc];
16135     },
16136
16137         /**
16138          * @ignore
16139          */
16140     getJsonAccessor: function(){
16141         var re = /[\[\.]/;
16142         return function(expr) {
16143             try {
16144                 return(re.test(expr))
16145                     ? new Function("obj", "return obj." + expr)
16146                     : function(obj){
16147                         return obj[expr];
16148                     };
16149             } catch(e){}
16150             return Roo.emptyFn;
16151         };
16152     }(),
16153
16154     /**
16155      * Create a data block containing Roo.data.Records from an XML document.
16156      * @param {Object} o An object which contains an Array of row objects in the property specified
16157      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16158      * which contains the total size of the dataset.
16159      * @return {Object} data A data block which is used by an Roo.data.Store object as
16160      * a cache of Roo.data.Records.
16161      */
16162     readRecords : function(o){
16163         /**
16164          * After any data loads, the raw JSON data is available for further custom processing.
16165          * @type Object
16166          */
16167         this.o = o;
16168         var s = this.meta, Record = this.recordType,
16169             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16170
16171 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16172         if (!this.ef) {
16173             if(s.totalProperty) {
16174                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16175                 }
16176                 if(s.successProperty) {
16177                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16178                 }
16179                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16180                 if (s.id) {
16181                         var g = this.getJsonAccessor(s.id);
16182                         this.getId = function(rec) {
16183                                 var r = g(rec);  
16184                                 return (r === undefined || r === "") ? null : r;
16185                         };
16186                 } else {
16187                         this.getId = function(){return null;};
16188                 }
16189             this.ef = [];
16190             for(var jj = 0; jj < fl; jj++){
16191                 f = fi[jj];
16192                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16193                 this.ef[jj] = this.getJsonAccessor(map);
16194             }
16195         }
16196
16197         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16198         if(s.totalProperty){
16199             var vt = parseInt(this.getTotal(o), 10);
16200             if(!isNaN(vt)){
16201                 totalRecords = vt;
16202             }
16203         }
16204         if(s.successProperty){
16205             var vs = this.getSuccess(o);
16206             if(vs === false || vs === 'false'){
16207                 success = false;
16208             }
16209         }
16210         var records = [];
16211         for(var i = 0; i < c; i++){
16212                 var n = root[i];
16213             var values = {};
16214             var id = this.getId(n);
16215             for(var j = 0; j < fl; j++){
16216                 f = fi[j];
16217             var v = this.ef[j](n);
16218             if (!f.convert) {
16219                 Roo.log('missing convert for ' + f.name);
16220                 Roo.log(f);
16221                 continue;
16222             }
16223             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16224             }
16225             var record = new Record(values, id);
16226             record.json = n;
16227             records[i] = record;
16228         }
16229         return {
16230             raw : o,
16231             success : success,
16232             records : records,
16233             totalRecords : totalRecords
16234         };
16235     },
16236     // used when loading children.. @see loadDataFromChildren
16237     toLoadData: function(rec)
16238     {
16239         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16240         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16241         return { data : data, total : data.length };
16242         
16243     }
16244 });/*
16245  * Based on:
16246  * Ext JS Library 1.1.1
16247  * Copyright(c) 2006-2007, Ext JS, LLC.
16248  *
16249  * Originally Released Under LGPL - original licence link has changed is not relivant.
16250  *
16251  * Fork - LGPL
16252  * <script type="text/javascript">
16253  */
16254
16255 /**
16256  * @class Roo.data.ArrayReader
16257  * @extends Roo.data.DataReader
16258  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16259  * Each element of that Array represents a row of data fields. The
16260  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16261  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16262  * <p>
16263  * Example code:.
16264  * <pre><code>
16265 var RecordDef = Roo.data.Record.create([
16266     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16267     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16268 ]);
16269 var myReader = new Roo.data.ArrayReader({
16270     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16271 }, RecordDef);
16272 </code></pre>
16273  * <p>
16274  * This would consume an Array like this:
16275  * <pre><code>
16276 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16277   </code></pre>
16278  
16279  * @constructor
16280  * Create a new JsonReader
16281  * @param {Object} meta Metadata configuration options.
16282  * @param {Object|Array} recordType Either an Array of field definition objects
16283  * 
16284  * @cfg {Array} fields Array of field definition objects
16285  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16286  * as specified to {@link Roo.data.Record#create},
16287  * or an {@link Roo.data.Record} object
16288  *
16289  * 
16290  * created using {@link Roo.data.Record#create}.
16291  */
16292 Roo.data.ArrayReader = function(meta, recordType)
16293 {    
16294     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16295 };
16296
16297 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16298     
16299       /**
16300      * Create a data block containing Roo.data.Records from an XML document.
16301      * @param {Object} o An Array of row objects which represents the dataset.
16302      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16303      * a cache of Roo.data.Records.
16304      */
16305     readRecords : function(o)
16306     {
16307         var sid = this.meta ? this.meta.id : null;
16308         var recordType = this.recordType, fields = recordType.prototype.fields;
16309         var records = [];
16310         var root = o;
16311         for(var i = 0; i < root.length; i++){
16312             var n = root[i];
16313             var values = {};
16314             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16315             for(var j = 0, jlen = fields.length; j < jlen; j++){
16316                 var f = fields.items[j];
16317                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16318                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16319                 v = f.convert(v);
16320                 values[f.name] = v;
16321             }
16322             var record = new recordType(values, id);
16323             record.json = n;
16324             records[records.length] = record;
16325         }
16326         return {
16327             records : records,
16328             totalRecords : records.length
16329         };
16330     },
16331     // used when loading children.. @see loadDataFromChildren
16332     toLoadData: function(rec)
16333     {
16334         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16335         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16336         
16337     }
16338     
16339     
16340 });/*
16341  * - LGPL
16342  * * 
16343  */
16344
16345 /**
16346  * @class Roo.bootstrap.ComboBox
16347  * @extends Roo.bootstrap.TriggerField
16348  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16349  * @cfg {Boolean} append (true|false) default false
16350  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16351  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16352  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16353  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16354  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16355  * @cfg {Boolean} animate default true
16356  * @cfg {Boolean} emptyResultText only for touch device
16357  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16358  * @cfg {String} emptyTitle default ''
16359  * @cfg {Number} width fixed with? experimental
16360  * @constructor
16361  * Create a new ComboBox.
16362  * @param {Object} config Configuration options
16363  */
16364 Roo.bootstrap.ComboBox = function(config){
16365     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16366     this.addEvents({
16367         /**
16368          * @event expand
16369          * Fires when the dropdown list is expanded
16370         * @param {Roo.bootstrap.ComboBox} combo This combo box
16371         */
16372         'expand' : true,
16373         /**
16374          * @event collapse
16375          * Fires when the dropdown list is collapsed
16376         * @param {Roo.bootstrap.ComboBox} combo This combo box
16377         */
16378         'collapse' : true,
16379         /**
16380          * @event beforeselect
16381          * Fires before a list item is selected. Return false to cancel the selection.
16382         * @param {Roo.bootstrap.ComboBox} combo This combo box
16383         * @param {Roo.data.Record} record The data record returned from the underlying store
16384         * @param {Number} index The index of the selected item in the dropdown list
16385         */
16386         'beforeselect' : true,
16387         /**
16388          * @event select
16389          * Fires when a list item is selected
16390         * @param {Roo.bootstrap.ComboBox} combo This combo box
16391         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16392         * @param {Number} index The index of the selected item in the dropdown list
16393         */
16394         'select' : true,
16395         /**
16396          * @event beforequery
16397          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16398          * The event object passed has these properties:
16399         * @param {Roo.bootstrap.ComboBox} combo This combo box
16400         * @param {String} query The query
16401         * @param {Boolean} forceAll true to force "all" query
16402         * @param {Boolean} cancel true to cancel the query
16403         * @param {Object} e The query event object
16404         */
16405         'beforequery': true,
16406          /**
16407          * @event add
16408          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16409         * @param {Roo.bootstrap.ComboBox} combo This combo box
16410         */
16411         'add' : true,
16412         /**
16413          * @event edit
16414          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16415         * @param {Roo.bootstrap.ComboBox} combo This combo box
16416         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16417         */
16418         'edit' : true,
16419         /**
16420          * @event remove
16421          * Fires when the remove value from the combobox array
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         */
16424         'remove' : true,
16425         /**
16426          * @event afterremove
16427          * Fires when the remove value from the combobox array
16428         * @param {Roo.bootstrap.ComboBox} combo This combo box
16429         */
16430         'afterremove' : true,
16431         /**
16432          * @event specialfilter
16433          * Fires when specialfilter
16434             * @param {Roo.bootstrap.ComboBox} combo This combo box
16435             */
16436         'specialfilter' : true,
16437         /**
16438          * @event tick
16439          * Fires when tick the element
16440             * @param {Roo.bootstrap.ComboBox} combo This combo box
16441             */
16442         'tick' : true,
16443         /**
16444          * @event touchviewdisplay
16445          * Fires when touch view require special display (default is using displayField)
16446             * @param {Roo.bootstrap.ComboBox} combo This combo box
16447             * @param {Object} cfg set html .
16448             */
16449         'touchviewdisplay' : true
16450         
16451     });
16452     
16453     this.item = [];
16454     this.tickItems = [];
16455     
16456     this.selectedIndex = -1;
16457     if(this.mode == 'local'){
16458         if(config.queryDelay === undefined){
16459             this.queryDelay = 10;
16460         }
16461         if(config.minChars === undefined){
16462             this.minChars = 0;
16463         }
16464     }
16465 };
16466
16467 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16468      
16469     /**
16470      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16471      * rendering into an Roo.Editor, defaults to false)
16472      */
16473     /**
16474      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16475      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16476      */
16477     /**
16478      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16479      */
16480     /**
16481      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16482      * the dropdown list (defaults to undefined, with no header element)
16483      */
16484
16485      /**
16486      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16487      */
16488      
16489      /**
16490      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16491      */
16492     listWidth: undefined,
16493     /**
16494      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16495      * mode = 'remote' or 'text' if mode = 'local')
16496      */
16497     displayField: undefined,
16498     
16499     /**
16500      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16501      * mode = 'remote' or 'value' if mode = 'local'). 
16502      * Note: use of a valueField requires the user make a selection
16503      * in order for a value to be mapped.
16504      */
16505     valueField: undefined,
16506     /**
16507      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16508      */
16509     modalTitle : '',
16510     
16511     /**
16512      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16513      * field's data value (defaults to the underlying DOM element's name)
16514      */
16515     hiddenName: undefined,
16516     /**
16517      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16518      */
16519     listClass: '',
16520     /**
16521      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16522      */
16523     selectedClass: 'active',
16524     
16525     /**
16526      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16527      */
16528     shadow:'sides',
16529     /**
16530      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16531      * anchor positions (defaults to 'tl-bl')
16532      */
16533     listAlign: 'tl-bl?',
16534     /**
16535      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16536      */
16537     maxHeight: 300,
16538     /**
16539      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16540      * query specified by the allQuery config option (defaults to 'query')
16541      */
16542     triggerAction: 'query',
16543     /**
16544      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16545      * (defaults to 4, does not apply if editable = false)
16546      */
16547     minChars : 4,
16548     /**
16549      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16550      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16551      */
16552     typeAhead: false,
16553     /**
16554      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16555      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16556      */
16557     queryDelay: 500,
16558     /**
16559      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16560      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16561      */
16562     pageSize: 0,
16563     /**
16564      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16565      * when editable = true (defaults to false)
16566      */
16567     selectOnFocus:false,
16568     /**
16569      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16570      */
16571     queryParam: 'query',
16572     /**
16573      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16574      * when mode = 'remote' (defaults to 'Loading...')
16575      */
16576     loadingText: 'Loading...',
16577     /**
16578      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16579      */
16580     resizable: false,
16581     /**
16582      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16583      */
16584     handleHeight : 8,
16585     /**
16586      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16587      * traditional select (defaults to true)
16588      */
16589     editable: true,
16590     /**
16591      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16592      */
16593     allQuery: '',
16594     /**
16595      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16596      */
16597     mode: 'remote',
16598     /**
16599      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16600      * listWidth has a higher value)
16601      */
16602     minListWidth : 70,
16603     /**
16604      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16605      * allow the user to set arbitrary text into the field (defaults to false)
16606      */
16607     forceSelection:false,
16608     /**
16609      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16610      * if typeAhead = true (defaults to 250)
16611      */
16612     typeAheadDelay : 250,
16613     /**
16614      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16615      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16616      */
16617     valueNotFoundText : undefined,
16618     /**
16619      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16620      */
16621     blockFocus : false,
16622     
16623     /**
16624      * @cfg {Boolean} disableClear Disable showing of clear button.
16625      */
16626     disableClear : false,
16627     /**
16628      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16629      */
16630     alwaysQuery : false,
16631     
16632     /**
16633      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16634      */
16635     multiple : false,
16636     
16637     /**
16638      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16639      */
16640     invalidClass : "has-warning",
16641     
16642     /**
16643      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16644      */
16645     validClass : "has-success",
16646     
16647     /**
16648      * @cfg {Boolean} specialFilter (true|false) special filter default false
16649      */
16650     specialFilter : false,
16651     
16652     /**
16653      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16654      */
16655     mobileTouchView : true,
16656     
16657     /**
16658      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16659      */
16660     useNativeIOS : false,
16661     
16662     /**
16663      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16664      */
16665     mobile_restrict_height : false,
16666     
16667     ios_options : false,
16668     
16669     //private
16670     addicon : false,
16671     editicon: false,
16672     
16673     page: 0,
16674     hasQuery: false,
16675     append: false,
16676     loadNext: false,
16677     autoFocus : true,
16678     tickable : false,
16679     btnPosition : 'right',
16680     triggerList : true,
16681     showToggleBtn : true,
16682     animate : true,
16683     emptyResultText: 'Empty',
16684     triggerText : 'Select',
16685     emptyTitle : '',
16686     width : false,
16687     
16688     // element that contains real text value.. (when hidden is used..)
16689     
16690     getAutoCreate : function()
16691     {   
16692         var cfg = false;
16693         //render
16694         /*
16695          * Render classic select for iso
16696          */
16697         
16698         if(Roo.isIOS && this.useNativeIOS){
16699             cfg = this.getAutoCreateNativeIOS();
16700             return cfg;
16701         }
16702         
16703         /*
16704          * Touch Devices
16705          */
16706         
16707         if(Roo.isTouch && this.mobileTouchView){
16708             cfg = this.getAutoCreateTouchView();
16709             return cfg;;
16710         }
16711         
16712         /*
16713          *  Normal ComboBox
16714          */
16715         if(!this.tickable){
16716             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16717             return cfg;
16718         }
16719         
16720         /*
16721          *  ComboBox with tickable selections
16722          */
16723              
16724         var align = this.labelAlign || this.parentLabelAlign();
16725         
16726         cfg = {
16727             cls : 'form-group roo-combobox-tickable' //input-group
16728         };
16729         
16730         var btn_text_select = '';
16731         var btn_text_done = '';
16732         var btn_text_cancel = '';
16733         
16734         if (this.btn_text_show) {
16735             btn_text_select = 'Select';
16736             btn_text_done = 'Done';
16737             btn_text_cancel = 'Cancel'; 
16738         }
16739         
16740         var buttons = {
16741             tag : 'div',
16742             cls : 'tickable-buttons',
16743             cn : [
16744                 {
16745                     tag : 'button',
16746                     type : 'button',
16747                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16748                     //html : this.triggerText
16749                     html: btn_text_select
16750                 },
16751                 {
16752                     tag : 'button',
16753                     type : 'button',
16754                     name : 'ok',
16755                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16756                     //html : 'Done'
16757                     html: btn_text_done
16758                 },
16759                 {
16760                     tag : 'button',
16761                     type : 'button',
16762                     name : 'cancel',
16763                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16764                     //html : 'Cancel'
16765                     html: btn_text_cancel
16766                 }
16767             ]
16768         };
16769         
16770         if(this.editable){
16771             buttons.cn.unshift({
16772                 tag: 'input',
16773                 cls: 'roo-select2-search-field-input'
16774             });
16775         }
16776         
16777         var _this = this;
16778         
16779         Roo.each(buttons.cn, function(c){
16780             if (_this.size) {
16781                 c.cls += ' btn-' + _this.size;
16782             }
16783
16784             if (_this.disabled) {
16785                 c.disabled = true;
16786             }
16787         });
16788         
16789         var box = {
16790             tag: 'div',
16791             style : 'display: contents',
16792             cn: [
16793                 {
16794                     tag: 'input',
16795                     type : 'hidden',
16796                     cls: 'form-hidden-field'
16797                 },
16798                 {
16799                     tag: 'ul',
16800                     cls: 'roo-select2-choices',
16801                     cn:[
16802                         {
16803                             tag: 'li',
16804                             cls: 'roo-select2-search-field',
16805                             cn: [
16806                                 buttons
16807                             ]
16808                         }
16809                     ]
16810                 }
16811             ]
16812         };
16813         
16814         var combobox = {
16815             cls: 'roo-select2-container input-group roo-select2-container-multi',
16816             cn: [
16817                 
16818                 box
16819 //                {
16820 //                    tag: 'ul',
16821 //                    cls: 'typeahead typeahead-long dropdown-menu',
16822 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16823 //                }
16824             ]
16825         };
16826         
16827         if(this.hasFeedback && !this.allowBlank){
16828             
16829             var feedback = {
16830                 tag: 'span',
16831                 cls: 'glyphicon form-control-feedback'
16832             };
16833
16834             combobox.cn.push(feedback);
16835         }
16836         
16837         
16838         
16839         var indicator = {
16840             tag : 'i',
16841             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16842             tooltip : 'This field is required'
16843         };
16844         if (Roo.bootstrap.version == 4) {
16845             indicator = {
16846                 tag : 'i',
16847                 style : 'display:none'
16848             };
16849         }
16850         if (align ==='left' && this.fieldLabel.length) {
16851             
16852             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16853             
16854             cfg.cn = [
16855                 indicator,
16856                 {
16857                     tag: 'label',
16858                     'for' :  id,
16859                     cls : 'control-label col-form-label',
16860                     html : this.fieldLabel
16861
16862                 },
16863                 {
16864                     cls : "", 
16865                     cn: [
16866                         combobox
16867                     ]
16868                 }
16869
16870             ];
16871             
16872             var labelCfg = cfg.cn[1];
16873             var contentCfg = cfg.cn[2];
16874             
16875
16876             if(this.indicatorpos == 'right'){
16877                 
16878                 cfg.cn = [
16879                     {
16880                         tag: 'label',
16881                         'for' :  id,
16882                         cls : 'control-label col-form-label',
16883                         cn : [
16884                             {
16885                                 tag : 'span',
16886                                 html : this.fieldLabel
16887                             },
16888                             indicator
16889                         ]
16890                     },
16891                     {
16892                         cls : "",
16893                         cn: [
16894                             combobox
16895                         ]
16896                     }
16897
16898                 ];
16899                 
16900                 
16901                 
16902                 labelCfg = cfg.cn[0];
16903                 contentCfg = cfg.cn[1];
16904             
16905             }
16906             
16907             if(this.labelWidth > 12){
16908                 labelCfg.style = "width: " + this.labelWidth + 'px';
16909             }
16910             if(this.width * 1 > 0){
16911                 contentCfg.style = "width: " + this.width + 'px';
16912             }
16913             if(this.labelWidth < 13 && this.labelmd == 0){
16914                 this.labelmd = this.labelWidth;
16915             }
16916             
16917             if(this.labellg > 0){
16918                 labelCfg.cls += ' col-lg-' + this.labellg;
16919                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16920             }
16921             
16922             if(this.labelmd > 0){
16923                 labelCfg.cls += ' col-md-' + this.labelmd;
16924                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16925             }
16926             
16927             if(this.labelsm > 0){
16928                 labelCfg.cls += ' col-sm-' + this.labelsm;
16929                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16930             }
16931             
16932             if(this.labelxs > 0){
16933                 labelCfg.cls += ' col-xs-' + this.labelxs;
16934                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16935             }
16936                 
16937                 
16938         } else if ( this.fieldLabel.length) {
16939 //                Roo.log(" label");
16940                  cfg.cn = [
16941                    indicator,
16942                     {
16943                         tag: 'label',
16944                         //cls : 'input-group-addon',
16945                         html : this.fieldLabel
16946                     },
16947                     combobox
16948                 ];
16949                 
16950                 if(this.indicatorpos == 'right'){
16951                     cfg.cn = [
16952                         {
16953                             tag: 'label',
16954                             //cls : 'input-group-addon',
16955                             html : this.fieldLabel
16956                         },
16957                         indicator,
16958                         combobox
16959                     ];
16960                     
16961                 }
16962
16963         } else {
16964             
16965 //                Roo.log(" no label && no align");
16966                 cfg = combobox
16967                      
16968                 
16969         }
16970          
16971         var settings=this;
16972         ['xs','sm','md','lg'].map(function(size){
16973             if (settings[size]) {
16974                 cfg.cls += ' col-' + size + '-' + settings[size];
16975             }
16976         });
16977         
16978         return cfg;
16979         
16980     },
16981     
16982     _initEventsCalled : false,
16983     
16984     // private
16985     initEvents: function()
16986     {   
16987         if (this._initEventsCalled) { // as we call render... prevent looping...
16988             return;
16989         }
16990         this._initEventsCalled = true;
16991         
16992         if (!this.store) {
16993             throw "can not find store for combo";
16994         }
16995         
16996         this.indicator = this.indicatorEl();
16997         
16998         this.store = Roo.factory(this.store, Roo.data);
16999         this.store.parent = this;
17000         
17001         // if we are building from html. then this element is so complex, that we can not really
17002         // use the rendered HTML.
17003         // so we have to trash and replace the previous code.
17004         if (Roo.XComponent.build_from_html) {
17005             // remove this element....
17006             var e = this.el.dom, k=0;
17007             while (e ) { e = e.previousSibling;  ++k;}
17008
17009             this.el.remove();
17010             
17011             this.el=false;
17012             this.rendered = false;
17013             
17014             this.render(this.parent().getChildContainer(true), k);
17015         }
17016         
17017         if(Roo.isIOS && this.useNativeIOS){
17018             this.initIOSView();
17019             return;
17020         }
17021         
17022         /*
17023          * Touch Devices
17024          */
17025         
17026         if(Roo.isTouch && this.mobileTouchView){
17027             this.initTouchView();
17028             return;
17029         }
17030         
17031         if(this.tickable){
17032             this.initTickableEvents();
17033             return;
17034         }
17035         
17036         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17037         
17038         if(this.hiddenName){
17039             
17040             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17041             
17042             this.hiddenField.dom.value =
17043                 this.hiddenValue !== undefined ? this.hiddenValue :
17044                 this.value !== undefined ? this.value : '';
17045
17046             // prevent input submission
17047             this.el.dom.removeAttribute('name');
17048             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17049              
17050              
17051         }
17052         //if(Roo.isGecko){
17053         //    this.el.dom.setAttribute('autocomplete', 'off');
17054         //}
17055         
17056         var cls = 'x-combo-list';
17057         
17058         //this.list = new Roo.Layer({
17059         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17060         //});
17061         
17062         var _this = this;
17063         
17064         (function(){
17065             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17066             _this.list.setWidth(lw);
17067         }).defer(100);
17068         
17069         this.list.on('mouseover', this.onViewOver, this);
17070         this.list.on('mousemove', this.onViewMove, this);
17071         this.list.on('scroll', this.onViewScroll, this);
17072         
17073         /*
17074         this.list.swallowEvent('mousewheel');
17075         this.assetHeight = 0;
17076
17077         if(this.title){
17078             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17079             this.assetHeight += this.header.getHeight();
17080         }
17081
17082         this.innerList = this.list.createChild({cls:cls+'-inner'});
17083         this.innerList.on('mouseover', this.onViewOver, this);
17084         this.innerList.on('mousemove', this.onViewMove, this);
17085         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17086         
17087         if(this.allowBlank && !this.pageSize && !this.disableClear){
17088             this.footer = this.list.createChild({cls:cls+'-ft'});
17089             this.pageTb = new Roo.Toolbar(this.footer);
17090            
17091         }
17092         if(this.pageSize){
17093             this.footer = this.list.createChild({cls:cls+'-ft'});
17094             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17095                     {pageSize: this.pageSize});
17096             
17097         }
17098         
17099         if (this.pageTb && this.allowBlank && !this.disableClear) {
17100             var _this = this;
17101             this.pageTb.add(new Roo.Toolbar.Fill(), {
17102                 cls: 'x-btn-icon x-btn-clear',
17103                 text: '&#160;',
17104                 handler: function()
17105                 {
17106                     _this.collapse();
17107                     _this.clearValue();
17108                     _this.onSelect(false, -1);
17109                 }
17110             });
17111         }
17112         if (this.footer) {
17113             this.assetHeight += this.footer.getHeight();
17114         }
17115         */
17116             
17117         if(!this.tpl){
17118             this.tpl = Roo.bootstrap.version == 4 ?
17119                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17120                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17121         }
17122
17123         this.view = new Roo.View(this.list, this.tpl, {
17124             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17125         });
17126         //this.view.wrapEl.setDisplayed(false);
17127         this.view.on('click', this.onViewClick, this);
17128         
17129         
17130         this.store.on('beforeload', this.onBeforeLoad, this);
17131         this.store.on('load', this.onLoad, this);
17132         this.store.on('loadexception', this.onLoadException, this);
17133         /*
17134         if(this.resizable){
17135             this.resizer = new Roo.Resizable(this.list,  {
17136                pinned:true, handles:'se'
17137             });
17138             this.resizer.on('resize', function(r, w, h){
17139                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17140                 this.listWidth = w;
17141                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17142                 this.restrictHeight();
17143             }, this);
17144             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17145         }
17146         */
17147         if(!this.editable){
17148             this.editable = true;
17149             this.setEditable(false);
17150         }
17151         
17152         /*
17153         
17154         if (typeof(this.events.add.listeners) != 'undefined') {
17155             
17156             this.addicon = this.wrap.createChild(
17157                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17158        
17159             this.addicon.on('click', function(e) {
17160                 this.fireEvent('add', this);
17161             }, this);
17162         }
17163         if (typeof(this.events.edit.listeners) != 'undefined') {
17164             
17165             this.editicon = this.wrap.createChild(
17166                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17167             if (this.addicon) {
17168                 this.editicon.setStyle('margin-left', '40px');
17169             }
17170             this.editicon.on('click', function(e) {
17171                 
17172                 // we fire even  if inothing is selected..
17173                 this.fireEvent('edit', this, this.lastData );
17174                 
17175             }, this);
17176         }
17177         */
17178         
17179         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17180             "up" : function(e){
17181                 this.inKeyMode = true;
17182                 this.selectPrev();
17183             },
17184
17185             "down" : function(e){
17186                 if(!this.isExpanded()){
17187                     this.onTriggerClick();
17188                 }else{
17189                     this.inKeyMode = true;
17190                     this.selectNext();
17191                 }
17192             },
17193
17194             "enter" : function(e){
17195 //                this.onViewClick();
17196                 //return true;
17197                 this.collapse();
17198                 
17199                 if(this.fireEvent("specialkey", this, e)){
17200                     this.onViewClick(false);
17201                 }
17202                 
17203                 return true;
17204             },
17205
17206             "esc" : function(e){
17207                 this.collapse();
17208             },
17209
17210             "tab" : function(e){
17211                 this.collapse();
17212                 
17213                 if(this.fireEvent("specialkey", this, e)){
17214                     this.onViewClick(false);
17215                 }
17216                 
17217                 return true;
17218             },
17219
17220             scope : this,
17221
17222             doRelay : function(foo, bar, hname){
17223                 if(hname == 'down' || this.scope.isExpanded()){
17224                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17225                 }
17226                 return true;
17227             },
17228
17229             forceKeyDown: true
17230         });
17231         
17232         
17233         this.queryDelay = Math.max(this.queryDelay || 10,
17234                 this.mode == 'local' ? 10 : 250);
17235         
17236         
17237         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17238         
17239         if(this.typeAhead){
17240             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17241         }
17242         if(this.editable !== false){
17243             this.inputEl().on("keyup", this.onKeyUp, this);
17244         }
17245         if(this.forceSelection){
17246             this.inputEl().on('blur', this.doForce, this);
17247         }
17248         
17249         if(this.multiple){
17250             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17251             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17252         }
17253     },
17254     
17255     initTickableEvents: function()
17256     {   
17257         this.createList();
17258         
17259         if(this.hiddenName){
17260             
17261             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17262             
17263             this.hiddenField.dom.value =
17264                 this.hiddenValue !== undefined ? this.hiddenValue :
17265                 this.value !== undefined ? this.value : '';
17266
17267             // prevent input submission
17268             this.el.dom.removeAttribute('name');
17269             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17270              
17271              
17272         }
17273         
17274 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17275         
17276         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17277         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17278         if(this.triggerList){
17279             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17280         }
17281          
17282         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17283         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17284         
17285         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17286         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17287         
17288         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17289         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17290         
17291         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17292         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17294         
17295         this.okBtn.hide();
17296         this.cancelBtn.hide();
17297         
17298         var _this = this;
17299         
17300         (function(){
17301             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17302             _this.list.setWidth(lw);
17303         }).defer(100);
17304         
17305         this.list.on('mouseover', this.onViewOver, this);
17306         this.list.on('mousemove', this.onViewMove, this);
17307         
17308         this.list.on('scroll', this.onViewScroll, this);
17309         
17310         if(!this.tpl){
17311             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17312                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17313         }
17314
17315         this.view = new Roo.View(this.list, this.tpl, {
17316             singleSelect:true,
17317             tickable:true,
17318             parent:this,
17319             store: this.store,
17320             selectedClass: this.selectedClass
17321         });
17322         
17323         //this.view.wrapEl.setDisplayed(false);
17324         this.view.on('click', this.onViewClick, this);
17325         
17326         
17327         
17328         this.store.on('beforeload', this.onBeforeLoad, this);
17329         this.store.on('load', this.onLoad, this);
17330         this.store.on('loadexception', this.onLoadException, this);
17331         
17332         if(this.editable){
17333             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17334                 "up" : function(e){
17335                     this.inKeyMode = true;
17336                     this.selectPrev();
17337                 },
17338
17339                 "down" : function(e){
17340                     this.inKeyMode = true;
17341                     this.selectNext();
17342                 },
17343
17344                 "enter" : function(e){
17345                     if(this.fireEvent("specialkey", this, e)){
17346                         this.onViewClick(false);
17347                     }
17348                     
17349                     return true;
17350                 },
17351
17352                 "esc" : function(e){
17353                     this.onTickableFooterButtonClick(e, false, false);
17354                 },
17355
17356                 "tab" : function(e){
17357                     this.fireEvent("specialkey", this, e);
17358                     
17359                     this.onTickableFooterButtonClick(e, false, false);
17360                     
17361                     return true;
17362                 },
17363
17364                 scope : this,
17365
17366                 doRelay : function(e, fn, key){
17367                     if(this.scope.isExpanded()){
17368                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17369                     }
17370                     return true;
17371                 },
17372
17373                 forceKeyDown: true
17374             });
17375         }
17376         
17377         this.queryDelay = Math.max(this.queryDelay || 10,
17378                 this.mode == 'local' ? 10 : 250);
17379         
17380         
17381         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17382         
17383         if(this.typeAhead){
17384             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17385         }
17386         
17387         if(this.editable !== false){
17388             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17389         }
17390         
17391         this.indicator = this.indicatorEl();
17392         
17393         if(this.indicator){
17394             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17395             this.indicator.hide();
17396         }
17397         
17398     },
17399
17400     onDestroy : function(){
17401         if(this.view){
17402             this.view.setStore(null);
17403             this.view.el.removeAllListeners();
17404             this.view.el.remove();
17405             this.view.purgeListeners();
17406         }
17407         if(this.list){
17408             this.list.dom.innerHTML  = '';
17409         }
17410         
17411         if(this.store){
17412             this.store.un('beforeload', this.onBeforeLoad, this);
17413             this.store.un('load', this.onLoad, this);
17414             this.store.un('loadexception', this.onLoadException, this);
17415         }
17416         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17417     },
17418
17419     // private
17420     fireKey : function(e){
17421         if(e.isNavKeyPress() && !this.list.isVisible()){
17422             this.fireEvent("specialkey", this, e);
17423         }
17424     },
17425
17426     // private
17427     onResize: function(w, h)
17428     {
17429         
17430         
17431 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17432 //        
17433 //        if(typeof w != 'number'){
17434 //            // we do not handle it!?!?
17435 //            return;
17436 //        }
17437 //        var tw = this.trigger.getWidth();
17438 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17439 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17440 //        var x = w - tw;
17441 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17442 //            
17443 //        //this.trigger.setStyle('left', x+'px');
17444 //        
17445 //        if(this.list && this.listWidth === undefined){
17446 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17447 //            this.list.setWidth(lw);
17448 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17449 //        }
17450         
17451     
17452         
17453     },
17454
17455     /**
17456      * Allow or prevent the user from directly editing the field text.  If false is passed,
17457      * the user will only be able to select from the items defined in the dropdown list.  This method
17458      * is the runtime equivalent of setting the 'editable' config option at config time.
17459      * @param {Boolean} value True to allow the user to directly edit the field text
17460      */
17461     setEditable : function(value){
17462         if(value == this.editable){
17463             return;
17464         }
17465         this.editable = value;
17466         if(!value){
17467             this.inputEl().dom.setAttribute('readOnly', true);
17468             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17469             this.inputEl().addClass('x-combo-noedit');
17470         }else{
17471             this.inputEl().dom.removeAttribute('readOnly');
17472             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17473             this.inputEl().removeClass('x-combo-noedit');
17474         }
17475     },
17476
17477     // private
17478     
17479     onBeforeLoad : function(combo,opts){
17480         if(!this.hasFocus){
17481             return;
17482         }
17483          if (!opts.add) {
17484             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17485          }
17486         this.restrictHeight();
17487         this.selectedIndex = -1;
17488     },
17489
17490     // private
17491     onLoad : function(){
17492         
17493         this.hasQuery = false;
17494         
17495         if(!this.hasFocus){
17496             return;
17497         }
17498         
17499         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17500             this.loading.hide();
17501         }
17502         
17503         if(this.store.getCount() > 0){
17504             
17505             this.expand();
17506             this.restrictHeight();
17507             if(this.lastQuery == this.allQuery){
17508                 if(this.editable && !this.tickable){
17509                     this.inputEl().dom.select();
17510                 }
17511                 
17512                 if(
17513                     !this.selectByValue(this.value, true) &&
17514                     this.autoFocus && 
17515                     (
17516                         !this.store.lastOptions ||
17517                         typeof(this.store.lastOptions.add) == 'undefined' || 
17518                         this.store.lastOptions.add != true
17519                     )
17520                 ){
17521                     this.select(0, true);
17522                 }
17523             }else{
17524                 if(this.autoFocus){
17525                     this.selectNext();
17526                 }
17527                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17528                     this.taTask.delay(this.typeAheadDelay);
17529                 }
17530             }
17531         }else{
17532             this.onEmptyResults();
17533         }
17534         
17535         //this.el.focus();
17536     },
17537     // private
17538     onLoadException : function()
17539     {
17540         this.hasQuery = false;
17541         
17542         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17543             this.loading.hide();
17544         }
17545         
17546         if(this.tickable && this.editable){
17547             return;
17548         }
17549         
17550         this.collapse();
17551         // only causes errors at present
17552         //Roo.log(this.store.reader.jsonData);
17553         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17554             // fixme
17555             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17556         //}
17557         
17558         
17559     },
17560     // private
17561     onTypeAhead : function(){
17562         if(this.store.getCount() > 0){
17563             var r = this.store.getAt(0);
17564             var newValue = r.data[this.displayField];
17565             var len = newValue.length;
17566             var selStart = this.getRawValue().length;
17567             
17568             if(selStart != len){
17569                 this.setRawValue(newValue);
17570                 this.selectText(selStart, newValue.length);
17571             }
17572         }
17573     },
17574
17575     // private
17576     onSelect : function(record, index){
17577         
17578         if(this.fireEvent('beforeselect', this, record, index) !== false){
17579         
17580             this.setFromData(index > -1 ? record.data : false);
17581             
17582             this.collapse();
17583             this.fireEvent('select', this, record, index);
17584         }
17585     },
17586
17587     /**
17588      * Returns the currently selected field value or empty string if no value is set.
17589      * @return {String} value The selected value
17590      */
17591     getValue : function()
17592     {
17593         if(Roo.isIOS && this.useNativeIOS){
17594             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17595         }
17596         
17597         if(this.multiple){
17598             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17599         }
17600         
17601         if(this.valueField){
17602             return typeof this.value != 'undefined' ? this.value : '';
17603         }else{
17604             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17605         }
17606     },
17607     
17608     getRawValue : function()
17609     {
17610         if(Roo.isIOS && this.useNativeIOS){
17611             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17612         }
17613         
17614         var v = this.inputEl().getValue();
17615         
17616         return v;
17617     },
17618
17619     /**
17620      * Clears any text/value currently set in the field
17621      */
17622     clearValue : function(){
17623         
17624         if(this.hiddenField){
17625             this.hiddenField.dom.value = '';
17626         }
17627         this.value = '';
17628         this.setRawValue('');
17629         this.lastSelectionText = '';
17630         this.lastData = false;
17631         
17632         var close = this.closeTriggerEl();
17633         
17634         if(close){
17635             close.hide();
17636         }
17637         
17638         this.validate();
17639         
17640     },
17641
17642     /**
17643      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17644      * will be displayed in the field.  If the value does not match the data value of an existing item,
17645      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17646      * Otherwise the field will be blank (although the value will still be set).
17647      * @param {String} value The value to match
17648      */
17649     setValue : function(v)
17650     {
17651         if(Roo.isIOS && this.useNativeIOS){
17652             this.setIOSValue(v);
17653             return;
17654         }
17655         
17656         if(this.multiple){
17657             this.syncValue();
17658             return;
17659         }
17660         
17661         var text = v;
17662         if(this.valueField){
17663             var r = this.findRecord(this.valueField, v);
17664             if(r){
17665                 text = r.data[this.displayField];
17666             }else if(this.valueNotFoundText !== undefined){
17667                 text = this.valueNotFoundText;
17668             }
17669         }
17670         this.lastSelectionText = text;
17671         if(this.hiddenField){
17672             this.hiddenField.dom.value = v;
17673         }
17674         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17675         this.value = v;
17676         
17677         var close = this.closeTriggerEl();
17678         
17679         if(close){
17680             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17681         }
17682         
17683         this.validate();
17684     },
17685     /**
17686      * @property {Object} the last set data for the element
17687      */
17688     
17689     lastData : false,
17690     /**
17691      * Sets the value of the field based on a object which is related to the record format for the store.
17692      * @param {Object} value the value to set as. or false on reset?
17693      */
17694     setFromData : function(o){
17695         
17696         if(this.multiple){
17697             this.addItem(o);
17698             return;
17699         }
17700             
17701         var dv = ''; // display value
17702         var vv = ''; // value value..
17703         this.lastData = o;
17704         if (this.displayField) {
17705             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17706         } else {
17707             // this is an error condition!!!
17708             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17709         }
17710         
17711         if(this.valueField){
17712             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17713         }
17714         
17715         var close = this.closeTriggerEl();
17716         
17717         if(close){
17718             if(dv.length || vv * 1 > 0){
17719                 close.show() ;
17720                 this.blockFocus=true;
17721             } else {
17722                 close.hide();
17723             }             
17724         }
17725         
17726         if(this.hiddenField){
17727             this.hiddenField.dom.value = vv;
17728             
17729             this.lastSelectionText = dv;
17730             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17731             this.value = vv;
17732             return;
17733         }
17734         // no hidden field.. - we store the value in 'value', but still display
17735         // display field!!!!
17736         this.lastSelectionText = dv;
17737         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17738         this.value = vv;
17739         
17740         
17741         
17742     },
17743     // private
17744     reset : function(){
17745         // overridden so that last data is reset..
17746         
17747         if(this.multiple){
17748             this.clearItem();
17749             return;
17750         }
17751         
17752         this.setValue(this.originalValue);
17753         //this.clearInvalid();
17754         this.lastData = false;
17755         if (this.view) {
17756             this.view.clearSelections();
17757         }
17758         
17759         this.validate();
17760     },
17761     // private
17762     findRecord : function(prop, value){
17763         var record;
17764         if(this.store.getCount() > 0){
17765             this.store.each(function(r){
17766                 if(r.data[prop] == value){
17767                     record = r;
17768                     return false;
17769                 }
17770                 return true;
17771             });
17772         }
17773         return record;
17774     },
17775     
17776     getName: function()
17777     {
17778         // returns hidden if it's set..
17779         if (!this.rendered) {return ''};
17780         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17781         
17782     },
17783     // private
17784     onViewMove : function(e, t){
17785         this.inKeyMode = false;
17786     },
17787
17788     // private
17789     onViewOver : function(e, t){
17790         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17791             return;
17792         }
17793         var item = this.view.findItemFromChild(t);
17794         
17795         if(item){
17796             var index = this.view.indexOf(item);
17797             this.select(index, false);
17798         }
17799     },
17800
17801     // private
17802     onViewClick : function(view, doFocus, el, e)
17803     {
17804         var index = this.view.getSelectedIndexes()[0];
17805         
17806         var r = this.store.getAt(index);
17807         
17808         if(this.tickable){
17809             
17810             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17811                 return;
17812             }
17813             
17814             var rm = false;
17815             var _this = this;
17816             
17817             Roo.each(this.tickItems, function(v,k){
17818                 
17819                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17820                     Roo.log(v);
17821                     _this.tickItems.splice(k, 1);
17822                     
17823                     if(typeof(e) == 'undefined' && view == false){
17824                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17825                     }
17826                     
17827                     rm = true;
17828                     return;
17829                 }
17830             });
17831             
17832             if(rm){
17833                 return;
17834             }
17835             
17836             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17837                 this.tickItems.push(r.data);
17838             }
17839             
17840             if(typeof(e) == 'undefined' && view == false){
17841                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17842             }
17843                     
17844             return;
17845         }
17846         
17847         if(r){
17848             this.onSelect(r, index);
17849         }
17850         if(doFocus !== false && !this.blockFocus){
17851             this.inputEl().focus();
17852         }
17853     },
17854
17855     // private
17856     restrictHeight : function(){
17857         //this.innerList.dom.style.height = '';
17858         //var inner = this.innerList.dom;
17859         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17860         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17861         //this.list.beginUpdate();
17862         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17863         this.list.alignTo(this.inputEl(), this.listAlign);
17864         this.list.alignTo(this.inputEl(), this.listAlign);
17865         //this.list.endUpdate();
17866     },
17867
17868     // private
17869     onEmptyResults : function(){
17870         
17871         if(this.tickable && this.editable){
17872             this.hasFocus = false;
17873             this.restrictHeight();
17874             return;
17875         }
17876         
17877         this.collapse();
17878     },
17879
17880     /**
17881      * Returns true if the dropdown list is expanded, else false.
17882      */
17883     isExpanded : function(){
17884         return this.list.isVisible();
17885     },
17886
17887     /**
17888      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17889      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17890      * @param {String} value The data value of the item to select
17891      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17892      * selected item if it is not currently in view (defaults to true)
17893      * @return {Boolean} True if the value matched an item in the list, else false
17894      */
17895     selectByValue : function(v, scrollIntoView){
17896         if(v !== undefined && v !== null){
17897             var r = this.findRecord(this.valueField || this.displayField, v);
17898             if(r){
17899                 this.select(this.store.indexOf(r), scrollIntoView);
17900                 return true;
17901             }
17902         }
17903         return false;
17904     },
17905
17906     /**
17907      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17908      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17909      * @param {Number} index The zero-based index of the list item to select
17910      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17911      * selected item if it is not currently in view (defaults to true)
17912      */
17913     select : function(index, scrollIntoView){
17914         this.selectedIndex = index;
17915         this.view.select(index);
17916         if(scrollIntoView !== false){
17917             var el = this.view.getNode(index);
17918             /*
17919              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17920              */
17921             if(el){
17922                 this.list.scrollChildIntoView(el, false);
17923             }
17924         }
17925     },
17926
17927     // private
17928     selectNext : function(){
17929         var ct = this.store.getCount();
17930         if(ct > 0){
17931             if(this.selectedIndex == -1){
17932                 this.select(0);
17933             }else if(this.selectedIndex < ct-1){
17934                 this.select(this.selectedIndex+1);
17935             }
17936         }
17937     },
17938
17939     // private
17940     selectPrev : function(){
17941         var ct = this.store.getCount();
17942         if(ct > 0){
17943             if(this.selectedIndex == -1){
17944                 this.select(0);
17945             }else if(this.selectedIndex != 0){
17946                 this.select(this.selectedIndex-1);
17947             }
17948         }
17949     },
17950
17951     // private
17952     onKeyUp : function(e){
17953         if(this.editable !== false && !e.isSpecialKey()){
17954             this.lastKey = e.getKey();
17955             this.dqTask.delay(this.queryDelay);
17956         }
17957     },
17958
17959     // private
17960     validateBlur : function(){
17961         return !this.list || !this.list.isVisible();   
17962     },
17963
17964     // private
17965     initQuery : function(){
17966         
17967         var v = this.getRawValue();
17968         
17969         if(this.tickable && this.editable){
17970             v = this.tickableInputEl().getValue();
17971         }
17972         
17973         this.doQuery(v);
17974     },
17975
17976     // private
17977     doForce : function(){
17978         if(this.inputEl().dom.value.length > 0){
17979             this.inputEl().dom.value =
17980                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17981              
17982         }
17983     },
17984
17985     /**
17986      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17987      * query allowing the query action to be canceled if needed.
17988      * @param {String} query The SQL query to execute
17989      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17990      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17991      * saved in the current store (defaults to false)
17992      */
17993     doQuery : function(q, forceAll){
17994         
17995         if(q === undefined || q === null){
17996             q = '';
17997         }
17998         var qe = {
17999             query: q,
18000             forceAll: forceAll,
18001             combo: this,
18002             cancel:false
18003         };
18004         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18005             return false;
18006         }
18007         q = qe.query;
18008         
18009         forceAll = qe.forceAll;
18010         if(forceAll === true || (q.length >= this.minChars)){
18011             
18012             this.hasQuery = true;
18013             
18014             if(this.lastQuery != q || this.alwaysQuery){
18015                 this.lastQuery = q;
18016                 if(this.mode == 'local'){
18017                     this.selectedIndex = -1;
18018                     if(forceAll){
18019                         this.store.clearFilter();
18020                     }else{
18021                         
18022                         if(this.specialFilter){
18023                             this.fireEvent('specialfilter', this);
18024                             this.onLoad();
18025                             return;
18026                         }
18027                         
18028                         this.store.filter(this.displayField, q);
18029                     }
18030                     
18031                     this.store.fireEvent("datachanged", this.store);
18032                     
18033                     this.onLoad();
18034                     
18035                     
18036                 }else{
18037                     
18038                     this.store.baseParams[this.queryParam] = q;
18039                     
18040                     var options = {params : this.getParams(q)};
18041                     
18042                     if(this.loadNext){
18043                         options.add = true;
18044                         options.params.start = this.page * this.pageSize;
18045                     }
18046                     
18047                     this.store.load(options);
18048                     
18049                     /*
18050                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18051                      *  we should expand the list on onLoad
18052                      *  so command out it
18053                      */
18054 //                    this.expand();
18055                 }
18056             }else{
18057                 this.selectedIndex = -1;
18058                 this.onLoad();   
18059             }
18060         }
18061         
18062         this.loadNext = false;
18063     },
18064     
18065     // private
18066     getParams : function(q){
18067         var p = {};
18068         //p[this.queryParam] = q;
18069         
18070         if(this.pageSize){
18071             p.start = 0;
18072             p.limit = this.pageSize;
18073         }
18074         return p;
18075     },
18076
18077     /**
18078      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18079      */
18080     collapse : function(){
18081         if(!this.isExpanded()){
18082             return;
18083         }
18084         
18085         this.list.hide();
18086         
18087         this.hasFocus = false;
18088         
18089         if(this.tickable){
18090             this.okBtn.hide();
18091             this.cancelBtn.hide();
18092             this.trigger.show();
18093             
18094             if(this.editable){
18095                 this.tickableInputEl().dom.value = '';
18096                 this.tickableInputEl().blur();
18097             }
18098             
18099         }
18100         
18101         Roo.get(document).un('mousedown', this.collapseIf, this);
18102         Roo.get(document).un('mousewheel', this.collapseIf, this);
18103         if (!this.editable) {
18104             Roo.get(document).un('keydown', this.listKeyPress, this);
18105         }
18106         this.fireEvent('collapse', this);
18107         
18108         this.validate();
18109     },
18110
18111     // private
18112     collapseIf : function(e){
18113         var in_combo  = e.within(this.el);
18114         var in_list =  e.within(this.list);
18115         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18116         
18117         if (in_combo || in_list || is_list) {
18118             //e.stopPropagation();
18119             return;
18120         }
18121         
18122         if(this.tickable){
18123             this.onTickableFooterButtonClick(e, false, false);
18124         }
18125
18126         this.collapse();
18127         
18128     },
18129
18130     /**
18131      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18132      */
18133     expand : function(){
18134        
18135         if(this.isExpanded() || !this.hasFocus){
18136             return;
18137         }
18138         
18139         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18140         this.list.setWidth(lw);
18141         
18142         Roo.log('expand');
18143         
18144         this.list.show();
18145         
18146         this.restrictHeight();
18147         
18148         if(this.tickable){
18149             
18150             this.tickItems = Roo.apply([], this.item);
18151             
18152             this.okBtn.show();
18153             this.cancelBtn.show();
18154             this.trigger.hide();
18155             
18156             if(this.editable){
18157                 this.tickableInputEl().focus();
18158             }
18159             
18160         }
18161         
18162         Roo.get(document).on('mousedown', this.collapseIf, this);
18163         Roo.get(document).on('mousewheel', this.collapseIf, this);
18164         if (!this.editable) {
18165             Roo.get(document).on('keydown', this.listKeyPress, this);
18166         }
18167         
18168         this.fireEvent('expand', this);
18169     },
18170
18171     // private
18172     // Implements the default empty TriggerField.onTriggerClick function
18173     onTriggerClick : function(e)
18174     {
18175         Roo.log('trigger click');
18176         
18177         if(this.disabled || !this.triggerList){
18178             return;
18179         }
18180         
18181         this.page = 0;
18182         this.loadNext = false;
18183         
18184         if(this.isExpanded()){
18185             this.collapse();
18186             if (!this.blockFocus) {
18187                 this.inputEl().focus();
18188             }
18189             
18190         }else {
18191             this.hasFocus = true;
18192             if(this.triggerAction == 'all') {
18193                 this.doQuery(this.allQuery, true);
18194             } else {
18195                 this.doQuery(this.getRawValue());
18196             }
18197             if (!this.blockFocus) {
18198                 this.inputEl().focus();
18199             }
18200         }
18201     },
18202     
18203     onTickableTriggerClick : function(e)
18204     {
18205         if(this.disabled){
18206             return;
18207         }
18208         
18209         this.page = 0;
18210         this.loadNext = false;
18211         this.hasFocus = true;
18212         
18213         if(this.triggerAction == 'all') {
18214             this.doQuery(this.allQuery, true);
18215         } else {
18216             this.doQuery(this.getRawValue());
18217         }
18218     },
18219     
18220     onSearchFieldClick : function(e)
18221     {
18222         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18223             this.onTickableFooterButtonClick(e, false, false);
18224             return;
18225         }
18226         
18227         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18228             return;
18229         }
18230         
18231         this.page = 0;
18232         this.loadNext = false;
18233         this.hasFocus = true;
18234         
18235         if(this.triggerAction == 'all') {
18236             this.doQuery(this.allQuery, true);
18237         } else {
18238             this.doQuery(this.getRawValue());
18239         }
18240     },
18241     
18242     listKeyPress : function(e)
18243     {
18244         //Roo.log('listkeypress');
18245         // scroll to first matching element based on key pres..
18246         if (e.isSpecialKey()) {
18247             return false;
18248         }
18249         var k = String.fromCharCode(e.getKey()).toUpperCase();
18250         //Roo.log(k);
18251         var match  = false;
18252         var csel = this.view.getSelectedNodes();
18253         var cselitem = false;
18254         if (csel.length) {
18255             var ix = this.view.indexOf(csel[0]);
18256             cselitem  = this.store.getAt(ix);
18257             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18258                 cselitem = false;
18259             }
18260             
18261         }
18262         
18263         this.store.each(function(v) { 
18264             if (cselitem) {
18265                 // start at existing selection.
18266                 if (cselitem.id == v.id) {
18267                     cselitem = false;
18268                 }
18269                 return true;
18270             }
18271                 
18272             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18273                 match = this.store.indexOf(v);
18274                 return false;
18275             }
18276             return true;
18277         }, this);
18278         
18279         if (match === false) {
18280             return true; // no more action?
18281         }
18282         // scroll to?
18283         this.view.select(match);
18284         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18285         sn.scrollIntoView(sn.dom.parentNode, false);
18286     },
18287     
18288     onViewScroll : function(e, t){
18289         
18290         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){
18291             return;
18292         }
18293         
18294         this.hasQuery = true;
18295         
18296         this.loading = this.list.select('.loading', true).first();
18297         
18298         if(this.loading === null){
18299             this.list.createChild({
18300                 tag: 'div',
18301                 cls: 'loading roo-select2-more-results roo-select2-active',
18302                 html: 'Loading more results...'
18303             });
18304             
18305             this.loading = this.list.select('.loading', true).first();
18306             
18307             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18308             
18309             this.loading.hide();
18310         }
18311         
18312         this.loading.show();
18313         
18314         var _combo = this;
18315         
18316         this.page++;
18317         this.loadNext = true;
18318         
18319         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18320         
18321         return;
18322     },
18323     
18324     addItem : function(o)
18325     {   
18326         var dv = ''; // display value
18327         
18328         if (this.displayField) {
18329             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18330         } else {
18331             // this is an error condition!!!
18332             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18333         }
18334         
18335         if(!dv.length){
18336             return;
18337         }
18338         
18339         var choice = this.choices.createChild({
18340             tag: 'li',
18341             cls: 'roo-select2-search-choice',
18342             cn: [
18343                 {
18344                     tag: 'div',
18345                     html: dv
18346                 },
18347                 {
18348                     tag: 'a',
18349                     href: '#',
18350                     cls: 'roo-select2-search-choice-close fa fa-times',
18351                     tabindex: '-1'
18352                 }
18353             ]
18354             
18355         }, this.searchField);
18356         
18357         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18358         
18359         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18360         
18361         this.item.push(o);
18362         
18363         this.lastData = o;
18364         
18365         this.syncValue();
18366         
18367         this.inputEl().dom.value = '';
18368         
18369         this.validate();
18370     },
18371     
18372     onRemoveItem : function(e, _self, o)
18373     {
18374         e.preventDefault();
18375         
18376         this.lastItem = Roo.apply([], this.item);
18377         
18378         var index = this.item.indexOf(o.data) * 1;
18379         
18380         if( index < 0){
18381             Roo.log('not this item?!');
18382             return;
18383         }
18384         
18385         this.item.splice(index, 1);
18386         o.item.remove();
18387         
18388         this.syncValue();
18389         
18390         this.fireEvent('remove', this, e);
18391         
18392         this.validate();
18393         
18394     },
18395     
18396     syncValue : function()
18397     {
18398         if(!this.item.length){
18399             this.clearValue();
18400             return;
18401         }
18402             
18403         var value = [];
18404         var _this = this;
18405         Roo.each(this.item, function(i){
18406             if(_this.valueField){
18407                 value.push(i[_this.valueField]);
18408                 return;
18409             }
18410
18411             value.push(i);
18412         });
18413
18414         this.value = value.join(',');
18415
18416         if(this.hiddenField){
18417             this.hiddenField.dom.value = this.value;
18418         }
18419         
18420         this.store.fireEvent("datachanged", this.store);
18421         
18422         this.validate();
18423     },
18424     
18425     clearItem : function()
18426     {
18427         if(!this.multiple){
18428             return;
18429         }
18430         
18431         this.item = [];
18432         
18433         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18434            c.remove();
18435         });
18436         
18437         this.syncValue();
18438         
18439         this.validate();
18440         
18441         if(this.tickable && !Roo.isTouch){
18442             this.view.refresh();
18443         }
18444     },
18445     
18446     inputEl: function ()
18447     {
18448         if(Roo.isIOS && this.useNativeIOS){
18449             return this.el.select('select.roo-ios-select', true).first();
18450         }
18451         
18452         if(Roo.isTouch && this.mobileTouchView){
18453             return this.el.select('input.form-control',true).first();
18454         }
18455         
18456         if(this.tickable){
18457             return this.searchField;
18458         }
18459         
18460         return this.el.select('input.form-control',true).first();
18461     },
18462     
18463     onTickableFooterButtonClick : function(e, btn, el)
18464     {
18465         e.preventDefault();
18466         
18467         this.lastItem = Roo.apply([], this.item);
18468         
18469         if(btn && btn.name == 'cancel'){
18470             this.tickItems = Roo.apply([], this.item);
18471             this.collapse();
18472             return;
18473         }
18474         
18475         this.clearItem();
18476         
18477         var _this = this;
18478         
18479         Roo.each(this.tickItems, function(o){
18480             _this.addItem(o);
18481         });
18482         
18483         this.collapse();
18484         
18485     },
18486     
18487     validate : function()
18488     {
18489         if(this.getVisibilityEl().hasClass('hidden')){
18490             return true;
18491         }
18492         
18493         var v = this.getRawValue();
18494         
18495         if(this.multiple){
18496             v = this.getValue();
18497         }
18498         
18499         if(this.disabled || this.allowBlank || v.length){
18500             this.markValid();
18501             return true;
18502         }
18503         
18504         this.markInvalid();
18505         return false;
18506     },
18507     
18508     tickableInputEl : function()
18509     {
18510         if(!this.tickable || !this.editable){
18511             return this.inputEl();
18512         }
18513         
18514         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18515     },
18516     
18517     
18518     getAutoCreateTouchView : function()
18519     {
18520         var id = Roo.id();
18521         
18522         var cfg = {
18523             cls: 'form-group' //input-group
18524         };
18525         
18526         var input =  {
18527             tag: 'input',
18528             id : id,
18529             type : this.inputType,
18530             cls : 'form-control x-combo-noedit',
18531             autocomplete: 'new-password',
18532             placeholder : this.placeholder || '',
18533             readonly : true
18534         };
18535         
18536         if (this.name) {
18537             input.name = this.name;
18538         }
18539         
18540         if (this.size) {
18541             input.cls += ' input-' + this.size;
18542         }
18543         
18544         if (this.disabled) {
18545             input.disabled = true;
18546         }
18547         
18548         var inputblock = {
18549             cls : 'roo-combobox-wrap',
18550             cn : [
18551                 input
18552             ]
18553         };
18554         
18555         if(this.before){
18556             inputblock.cls += ' input-group';
18557             
18558             inputblock.cn.unshift({
18559                 tag :'span',
18560                 cls : 'input-group-addon input-group-prepend input-group-text',
18561                 html : this.before
18562             });
18563         }
18564         
18565         if(this.removable && !this.multiple){
18566             inputblock.cls += ' roo-removable';
18567             
18568             inputblock.cn.push({
18569                 tag: 'button',
18570                 html : 'x',
18571                 cls : 'roo-combo-removable-btn close'
18572             });
18573         }
18574
18575         if(this.hasFeedback && !this.allowBlank){
18576             
18577             inputblock.cls += ' has-feedback';
18578             
18579             inputblock.cn.push({
18580                 tag: 'span',
18581                 cls: 'glyphicon form-control-feedback'
18582             });
18583             
18584         }
18585         
18586         if (this.after) {
18587             
18588             inputblock.cls += (this.before) ? '' : ' input-group';
18589             
18590             inputblock.cn.push({
18591                 tag :'span',
18592                 cls : 'input-group-addon input-group-append input-group-text',
18593                 html : this.after
18594             });
18595         }
18596
18597         
18598         var ibwrap = inputblock;
18599         
18600         if(this.multiple){
18601             ibwrap = {
18602                 tag: 'ul',
18603                 cls: 'roo-select2-choices',
18604                 cn:[
18605                     {
18606                         tag: 'li',
18607                         cls: 'roo-select2-search-field',
18608                         cn: [
18609
18610                             inputblock
18611                         ]
18612                     }
18613                 ]
18614             };
18615         
18616             
18617         }
18618         
18619         var combobox = {
18620             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18621             cn: [
18622                 {
18623                     tag: 'input',
18624                     type : 'hidden',
18625                     cls: 'form-hidden-field'
18626                 },
18627                 ibwrap
18628             ]
18629         };
18630         
18631         if(!this.multiple && this.showToggleBtn){
18632             
18633             var caret = {
18634                 cls: 'caret'
18635             };
18636             
18637             if (this.caret != false) {
18638                 caret = {
18639                      tag: 'i',
18640                      cls: 'fa fa-' + this.caret
18641                 };
18642                 
18643             }
18644             
18645             combobox.cn.push({
18646                 tag :'span',
18647                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18648                 cn : [
18649                     Roo.bootstrap.version == 3 ? caret : '',
18650                     {
18651                         tag: 'span',
18652                         cls: 'combobox-clear',
18653                         cn  : [
18654                             {
18655                                 tag : 'i',
18656                                 cls: 'icon-remove'
18657                             }
18658                         ]
18659                     }
18660                 ]
18661
18662             })
18663         }
18664         
18665         if(this.multiple){
18666             combobox.cls += ' roo-select2-container-multi';
18667         }
18668         
18669         var required =  this.allowBlank ?  {
18670                     tag : 'i',
18671                     style: 'display: none'
18672                 } : {
18673                    tag : 'i',
18674                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18675                    tooltip : 'This field is required'
18676                 };
18677         
18678         var align = this.labelAlign || this.parentLabelAlign();
18679         
18680         if (align ==='left' && this.fieldLabel.length) {
18681
18682             cfg.cn = [
18683                 required,
18684                 {
18685                     tag: 'label',
18686                     cls : 'control-label col-form-label',
18687                     html : this.fieldLabel
18688
18689                 },
18690                 {
18691                     cls : 'roo-combobox-wrap ', 
18692                     cn: [
18693                         combobox
18694                     ]
18695                 }
18696             ];
18697             
18698             var labelCfg = cfg.cn[1];
18699             var contentCfg = cfg.cn[2];
18700             
18701
18702             if(this.indicatorpos == 'right'){
18703                 cfg.cn = [
18704                     {
18705                         tag: 'label',
18706                         'for' :  id,
18707                         cls : 'control-label col-form-label',
18708                         cn : [
18709                             {
18710                                 tag : 'span',
18711                                 html : this.fieldLabel
18712                             },
18713                             required
18714                         ]
18715                     },
18716                     {
18717                         cls : "roo-combobox-wrap ",
18718                         cn: [
18719                             combobox
18720                         ]
18721                     }
18722
18723                 ];
18724                 
18725                 labelCfg = cfg.cn[0];
18726                 contentCfg = cfg.cn[1];
18727             }
18728             
18729            
18730             
18731             if(this.labelWidth > 12){
18732                 labelCfg.style = "width: " + this.labelWidth + 'px';
18733             }
18734            
18735             if(this.labelWidth < 13 && this.labelmd == 0){
18736                 this.labelmd = this.labelWidth;
18737             }
18738             
18739             if(this.labellg > 0){
18740                 labelCfg.cls += ' col-lg-' + this.labellg;
18741                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18742             }
18743             
18744             if(this.labelmd > 0){
18745                 labelCfg.cls += ' col-md-' + this.labelmd;
18746                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18747             }
18748             
18749             if(this.labelsm > 0){
18750                 labelCfg.cls += ' col-sm-' + this.labelsm;
18751                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18752             }
18753             
18754             if(this.labelxs > 0){
18755                 labelCfg.cls += ' col-xs-' + this.labelxs;
18756                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18757             }
18758                 
18759                 
18760         } else if ( this.fieldLabel.length) {
18761             cfg.cn = [
18762                required,
18763                 {
18764                     tag: 'label',
18765                     cls : 'control-label',
18766                     html : this.fieldLabel
18767
18768                 },
18769                 {
18770                     cls : '', 
18771                     cn: [
18772                         combobox
18773                     ]
18774                 }
18775             ];
18776             
18777             if(this.indicatorpos == 'right'){
18778                 cfg.cn = [
18779                     {
18780                         tag: 'label',
18781                         cls : 'control-label',
18782                         html : this.fieldLabel,
18783                         cn : [
18784                             required
18785                         ]
18786                     },
18787                     {
18788                         cls : '', 
18789                         cn: [
18790                             combobox
18791                         ]
18792                     }
18793                 ];
18794             }
18795         } else {
18796             cfg.cn = combobox;    
18797         }
18798         
18799         
18800         var settings = this;
18801         
18802         ['xs','sm','md','lg'].map(function(size){
18803             if (settings[size]) {
18804                 cfg.cls += ' col-' + size + '-' + settings[size];
18805             }
18806         });
18807         
18808         return cfg;
18809     },
18810     
18811     initTouchView : function()
18812     {
18813         this.renderTouchView();
18814         
18815         this.touchViewEl.on('scroll', function(){
18816             this.el.dom.scrollTop = 0;
18817         }, this);
18818         
18819         this.originalValue = this.getValue();
18820         
18821         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18822         
18823         this.inputEl().on("click", this.showTouchView, this);
18824         if (this.triggerEl) {
18825             this.triggerEl.on("click", this.showTouchView, this);
18826         }
18827         
18828         
18829         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18830         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18831         
18832         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18833         
18834         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18835         this.store.on('load', this.onTouchViewLoad, this);
18836         this.store.on('loadexception', this.onTouchViewLoadException, this);
18837         
18838         if(this.hiddenName){
18839             
18840             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18841             
18842             this.hiddenField.dom.value =
18843                 this.hiddenValue !== undefined ? this.hiddenValue :
18844                 this.value !== undefined ? this.value : '';
18845         
18846             this.el.dom.removeAttribute('name');
18847             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18848         }
18849         
18850         if(this.multiple){
18851             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18852             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18853         }
18854         
18855         if(this.removable && !this.multiple){
18856             var close = this.closeTriggerEl();
18857             if(close){
18858                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18859                 close.on('click', this.removeBtnClick, this, close);
18860             }
18861         }
18862         /*
18863          * fix the bug in Safari iOS8
18864          */
18865         this.inputEl().on("focus", function(e){
18866             document.activeElement.blur();
18867         }, this);
18868         
18869         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18870         
18871         return;
18872         
18873         
18874     },
18875     
18876     renderTouchView : function()
18877     {
18878         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18879         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880         
18881         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18882         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883         
18884         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18885         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886         this.touchViewBodyEl.setStyle('overflow', 'auto');
18887         
18888         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18889         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         
18891         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18892         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893         
18894     },
18895     
18896     showTouchView : function()
18897     {
18898         if(this.disabled){
18899             return;
18900         }
18901         
18902         this.touchViewHeaderEl.hide();
18903
18904         if(this.modalTitle.length){
18905             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18906             this.touchViewHeaderEl.show();
18907         }
18908
18909         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18910         this.touchViewEl.show();
18911
18912         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18913         
18914         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18915         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18916
18917         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18918
18919         if(this.modalTitle.length){
18920             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18921         }
18922         
18923         this.touchViewBodyEl.setHeight(bodyHeight);
18924
18925         if(this.animate){
18926             var _this = this;
18927             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18928         }else{
18929             this.touchViewEl.addClass(['in','show']);
18930         }
18931         
18932         if(this._touchViewMask){
18933             Roo.get(document.body).addClass("x-body-masked");
18934             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18935             this._touchViewMask.setStyle('z-index', 10000);
18936             this._touchViewMask.addClass('show');
18937         }
18938         
18939         this.doTouchViewQuery();
18940         
18941     },
18942     
18943     hideTouchView : function()
18944     {
18945         this.touchViewEl.removeClass(['in','show']);
18946
18947         if(this.animate){
18948             var _this = this;
18949             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18950         }else{
18951             this.touchViewEl.setStyle('display', 'none');
18952         }
18953         
18954         if(this._touchViewMask){
18955             this._touchViewMask.removeClass('show');
18956             Roo.get(document.body).removeClass("x-body-masked");
18957         }
18958     },
18959     
18960     setTouchViewValue : function()
18961     {
18962         if(this.multiple){
18963             this.clearItem();
18964         
18965             var _this = this;
18966
18967             Roo.each(this.tickItems, function(o){
18968                 this.addItem(o);
18969             }, this);
18970         }
18971         
18972         this.hideTouchView();
18973     },
18974     
18975     doTouchViewQuery : function()
18976     {
18977         var qe = {
18978             query: '',
18979             forceAll: true,
18980             combo: this,
18981             cancel:false
18982         };
18983         
18984         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18985             return false;
18986         }
18987         
18988         if(!this.alwaysQuery || this.mode == 'local'){
18989             this.onTouchViewLoad();
18990             return;
18991         }
18992         
18993         this.store.load();
18994     },
18995     
18996     onTouchViewBeforeLoad : function(combo,opts)
18997     {
18998         return;
18999     },
19000
19001     // private
19002     onTouchViewLoad : function()
19003     {
19004         if(this.store.getCount() < 1){
19005             this.onTouchViewEmptyResults();
19006             return;
19007         }
19008         
19009         this.clearTouchView();
19010         
19011         var rawValue = this.getRawValue();
19012         
19013         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19014         
19015         this.tickItems = [];
19016         
19017         this.store.data.each(function(d, rowIndex){
19018             var row = this.touchViewListGroup.createChild(template);
19019             
19020             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19021                 row.addClass(d.data.cls);
19022             }
19023             
19024             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19025                 var cfg = {
19026                     data : d.data,
19027                     html : d.data[this.displayField]
19028                 };
19029                 
19030                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19031                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19032                 }
19033             }
19034             row.removeClass('selected');
19035             if(!this.multiple && this.valueField &&
19036                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19037             {
19038                 // radio buttons..
19039                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19040                 row.addClass('selected');
19041             }
19042             
19043             if(this.multiple && this.valueField &&
19044                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19045             {
19046                 
19047                 // checkboxes...
19048                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19049                 this.tickItems.push(d.data);
19050             }
19051             
19052             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19053             
19054         }, this);
19055         
19056         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19057         
19058         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19059
19060         if(this.modalTitle.length){
19061             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19062         }
19063
19064         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19065         
19066         if(this.mobile_restrict_height && listHeight < bodyHeight){
19067             this.touchViewBodyEl.setHeight(listHeight);
19068         }
19069         
19070         var _this = this;
19071         
19072         if(firstChecked && listHeight > bodyHeight){
19073             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19074         }
19075         
19076     },
19077     
19078     onTouchViewLoadException : function()
19079     {
19080         this.hideTouchView();
19081     },
19082     
19083     onTouchViewEmptyResults : function()
19084     {
19085         this.clearTouchView();
19086         
19087         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19088         
19089         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19090         
19091     },
19092     
19093     clearTouchView : function()
19094     {
19095         this.touchViewListGroup.dom.innerHTML = '';
19096     },
19097     
19098     onTouchViewClick : function(e, el, o)
19099     {
19100         e.preventDefault();
19101         
19102         var row = o.row;
19103         var rowIndex = o.rowIndex;
19104         
19105         var r = this.store.getAt(rowIndex);
19106         
19107         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19108             
19109             if(!this.multiple){
19110                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19111                     c.dom.removeAttribute('checked');
19112                 }, this);
19113
19114                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19115
19116                 this.setFromData(r.data);
19117
19118                 var close = this.closeTriggerEl();
19119
19120                 if(close){
19121                     close.show();
19122                 }
19123
19124                 this.hideTouchView();
19125
19126                 this.fireEvent('select', this, r, rowIndex);
19127
19128                 return;
19129             }
19130
19131             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19132                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19133                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19134                 return;
19135             }
19136
19137             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19138             this.addItem(r.data);
19139             this.tickItems.push(r.data);
19140         }
19141     },
19142     
19143     getAutoCreateNativeIOS : function()
19144     {
19145         var cfg = {
19146             cls: 'form-group' //input-group,
19147         };
19148         
19149         var combobox =  {
19150             tag: 'select',
19151             cls : 'roo-ios-select'
19152         };
19153         
19154         if (this.name) {
19155             combobox.name = this.name;
19156         }
19157         
19158         if (this.disabled) {
19159             combobox.disabled = true;
19160         }
19161         
19162         var settings = this;
19163         
19164         ['xs','sm','md','lg'].map(function(size){
19165             if (settings[size]) {
19166                 cfg.cls += ' col-' + size + '-' + settings[size];
19167             }
19168         });
19169         
19170         cfg.cn = combobox;
19171         
19172         return cfg;
19173         
19174     },
19175     
19176     initIOSView : function()
19177     {
19178         this.store.on('load', this.onIOSViewLoad, this);
19179         
19180         return;
19181     },
19182     
19183     onIOSViewLoad : function()
19184     {
19185         if(this.store.getCount() < 1){
19186             return;
19187         }
19188         
19189         this.clearIOSView();
19190         
19191         if(this.allowBlank) {
19192             
19193             var default_text = '-- SELECT --';
19194             
19195             if(this.placeholder.length){
19196                 default_text = this.placeholder;
19197             }
19198             
19199             if(this.emptyTitle.length){
19200                 default_text += ' - ' + this.emptyTitle + ' -';
19201             }
19202             
19203             var opt = this.inputEl().createChild({
19204                 tag: 'option',
19205                 value : 0,
19206                 html : default_text
19207             });
19208             
19209             var o = {};
19210             o[this.valueField] = 0;
19211             o[this.displayField] = default_text;
19212             
19213             this.ios_options.push({
19214                 data : o,
19215                 el : opt
19216             });
19217             
19218         }
19219         
19220         this.store.data.each(function(d, rowIndex){
19221             
19222             var html = '';
19223             
19224             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19225                 html = d.data[this.displayField];
19226             }
19227             
19228             var value = '';
19229             
19230             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19231                 value = d.data[this.valueField];
19232             }
19233             
19234             var option = {
19235                 tag: 'option',
19236                 value : value,
19237                 html : html
19238             };
19239             
19240             if(this.value == d.data[this.valueField]){
19241                 option['selected'] = true;
19242             }
19243             
19244             var opt = this.inputEl().createChild(option);
19245             
19246             this.ios_options.push({
19247                 data : d.data,
19248                 el : opt
19249             });
19250             
19251         }, this);
19252         
19253         this.inputEl().on('change', function(){
19254            this.fireEvent('select', this);
19255         }, this);
19256         
19257     },
19258     
19259     clearIOSView: function()
19260     {
19261         this.inputEl().dom.innerHTML = '';
19262         
19263         this.ios_options = [];
19264     },
19265     
19266     setIOSValue: function(v)
19267     {
19268         this.value = v;
19269         
19270         if(!this.ios_options){
19271             return;
19272         }
19273         
19274         Roo.each(this.ios_options, function(opts){
19275            
19276            opts.el.dom.removeAttribute('selected');
19277            
19278            if(opts.data[this.valueField] != v){
19279                return;
19280            }
19281            
19282            opts.el.dom.setAttribute('selected', true);
19283            
19284         }, this);
19285     }
19286
19287     /** 
19288     * @cfg {Boolean} grow 
19289     * @hide 
19290     */
19291     /** 
19292     * @cfg {Number} growMin 
19293     * @hide 
19294     */
19295     /** 
19296     * @cfg {Number} growMax 
19297     * @hide 
19298     */
19299     /**
19300      * @hide
19301      * @method autoSize
19302      */
19303 });
19304
19305 Roo.apply(Roo.bootstrap.ComboBox,  {
19306     
19307     header : {
19308         tag: 'div',
19309         cls: 'modal-header',
19310         cn: [
19311             {
19312                 tag: 'h4',
19313                 cls: 'modal-title'
19314             }
19315         ]
19316     },
19317     
19318     body : {
19319         tag: 'div',
19320         cls: 'modal-body',
19321         cn: [
19322             {
19323                 tag: 'ul',
19324                 cls: 'list-group'
19325             }
19326         ]
19327     },
19328     
19329     listItemRadio : {
19330         tag: 'li',
19331         cls: 'list-group-item',
19332         cn: [
19333             {
19334                 tag: 'span',
19335                 cls: 'roo-combobox-list-group-item-value'
19336             },
19337             {
19338                 tag: 'div',
19339                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19340                 cn: [
19341                     {
19342                         tag: 'input',
19343                         type: 'radio'
19344                     },
19345                     {
19346                         tag: 'label'
19347                     }
19348                 ]
19349             }
19350         ]
19351     },
19352     
19353     listItemCheckbox : {
19354         tag: 'li',
19355         cls: 'list-group-item',
19356         cn: [
19357             {
19358                 tag: 'span',
19359                 cls: 'roo-combobox-list-group-item-value'
19360             },
19361             {
19362                 tag: 'div',
19363                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19364                 cn: [
19365                     {
19366                         tag: 'input',
19367                         type: 'checkbox'
19368                     },
19369                     {
19370                         tag: 'label'
19371                     }
19372                 ]
19373             }
19374         ]
19375     },
19376     
19377     emptyResult : {
19378         tag: 'div',
19379         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19380     },
19381     
19382     footer : {
19383         tag: 'div',
19384         cls: 'modal-footer',
19385         cn: [
19386             {
19387                 tag: 'div',
19388                 cls: 'row',
19389                 cn: [
19390                     {
19391                         tag: 'div',
19392                         cls: 'col-xs-6 text-left',
19393                         cn: {
19394                             tag: 'button',
19395                             cls: 'btn btn-danger roo-touch-view-cancel',
19396                             html: 'Cancel'
19397                         }
19398                     },
19399                     {
19400                         tag: 'div',
19401                         cls: 'col-xs-6 text-right',
19402                         cn: {
19403                             tag: 'button',
19404                             cls: 'btn btn-success roo-touch-view-ok',
19405                             html: 'OK'
19406                         }
19407                     }
19408                 ]
19409             }
19410         ]
19411         
19412     }
19413 });
19414
19415 Roo.apply(Roo.bootstrap.ComboBox,  {
19416     
19417     touchViewTemplate : {
19418         tag: 'div',
19419         cls: 'modal fade roo-combobox-touch-view',
19420         cn: [
19421             {
19422                 tag: 'div',
19423                 cls: 'modal-dialog',
19424                 style : 'position:fixed', // we have to fix position....
19425                 cn: [
19426                     {
19427                         tag: 'div',
19428                         cls: 'modal-content',
19429                         cn: [
19430                             Roo.bootstrap.ComboBox.header,
19431                             Roo.bootstrap.ComboBox.body,
19432                             Roo.bootstrap.ComboBox.footer
19433                         ]
19434                     }
19435                 ]
19436             }
19437         ]
19438     }
19439 });/*
19440  * Based on:
19441  * Ext JS Library 1.1.1
19442  * Copyright(c) 2006-2007, Ext JS, LLC.
19443  *
19444  * Originally Released Under LGPL - original licence link has changed is not relivant.
19445  *
19446  * Fork - LGPL
19447  * <script type="text/javascript">
19448  */
19449
19450 /**
19451  * @class Roo.View
19452  * @extends Roo.util.Observable
19453  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19454  * This class also supports single and multi selection modes. <br>
19455  * Create a data model bound view:
19456  <pre><code>
19457  var store = new Roo.data.Store(...);
19458
19459  var view = new Roo.View({
19460     el : "my-element",
19461     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19462  
19463     singleSelect: true,
19464     selectedClass: "ydataview-selected",
19465     store: store
19466  });
19467
19468  // listen for node click?
19469  view.on("click", function(vw, index, node, e){
19470  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19471  });
19472
19473  // load XML data
19474  dataModel.load("foobar.xml");
19475  </code></pre>
19476  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19477  * <br><br>
19478  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19479  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19480  * 
19481  * Note: old style constructor is still suported (container, template, config)
19482  * 
19483  * @constructor
19484  * Create a new View
19485  * @param {Object} config The config object
19486  * 
19487  */
19488 Roo.View = function(config, depreciated_tpl, depreciated_config){
19489     
19490     this.parent = false;
19491     
19492     if (typeof(depreciated_tpl) == 'undefined') {
19493         // new way.. - universal constructor.
19494         Roo.apply(this, config);
19495         this.el  = Roo.get(this.el);
19496     } else {
19497         // old format..
19498         this.el  = Roo.get(config);
19499         this.tpl = depreciated_tpl;
19500         Roo.apply(this, depreciated_config);
19501     }
19502     this.wrapEl  = this.el.wrap().wrap();
19503     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19504     
19505     
19506     if(typeof(this.tpl) == "string"){
19507         this.tpl = new Roo.Template(this.tpl);
19508     } else {
19509         // support xtype ctors..
19510         this.tpl = new Roo.factory(this.tpl, Roo);
19511     }
19512     
19513     
19514     this.tpl.compile();
19515     
19516     /** @private */
19517     this.addEvents({
19518         /**
19519          * @event beforeclick
19520          * Fires before a click is processed. Returns false to cancel the default action.
19521          * @param {Roo.View} this
19522          * @param {Number} index The index of the target node
19523          * @param {HTMLElement} node The target node
19524          * @param {Roo.EventObject} e The raw event object
19525          */
19526             "beforeclick" : true,
19527         /**
19528          * @event click
19529          * Fires when a template node is clicked.
19530          * @param {Roo.View} this
19531          * @param {Number} index The index of the target node
19532          * @param {HTMLElement} node The target node
19533          * @param {Roo.EventObject} e The raw event object
19534          */
19535             "click" : true,
19536         /**
19537          * @event dblclick
19538          * Fires when a template node is double clicked.
19539          * @param {Roo.View} this
19540          * @param {Number} index The index of the target node
19541          * @param {HTMLElement} node The target node
19542          * @param {Roo.EventObject} e The raw event object
19543          */
19544             "dblclick" : true,
19545         /**
19546          * @event contextmenu
19547          * Fires when a template node is right clicked.
19548          * @param {Roo.View} this
19549          * @param {Number} index The index of the target node
19550          * @param {HTMLElement} node The target node
19551          * @param {Roo.EventObject} e The raw event object
19552          */
19553             "contextmenu" : true,
19554         /**
19555          * @event selectionchange
19556          * Fires when the selected nodes change.
19557          * @param {Roo.View} this
19558          * @param {Array} selections Array of the selected nodes
19559          */
19560             "selectionchange" : true,
19561     
19562         /**
19563          * @event beforeselect
19564          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19565          * @param {Roo.View} this
19566          * @param {HTMLElement} node The node to be selected
19567          * @param {Array} selections Array of currently selected nodes
19568          */
19569             "beforeselect" : true,
19570         /**
19571          * @event preparedata
19572          * Fires on every row to render, to allow you to change the data.
19573          * @param {Roo.View} this
19574          * @param {Object} data to be rendered (change this)
19575          */
19576           "preparedata" : true
19577           
19578           
19579         });
19580
19581
19582
19583     this.el.on({
19584         "click": this.onClick,
19585         "dblclick": this.onDblClick,
19586         "contextmenu": this.onContextMenu,
19587         scope:this
19588     });
19589
19590     this.selections = [];
19591     this.nodes = [];
19592     this.cmp = new Roo.CompositeElementLite([]);
19593     if(this.store){
19594         this.store = Roo.factory(this.store, Roo.data);
19595         this.setStore(this.store, true);
19596     }
19597     
19598     if ( this.footer && this.footer.xtype) {
19599            
19600          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19601         
19602         this.footer.dataSource = this.store;
19603         this.footer.container = fctr;
19604         this.footer = Roo.factory(this.footer, Roo);
19605         fctr.insertFirst(this.el);
19606         
19607         // this is a bit insane - as the paging toolbar seems to detach the el..
19608 //        dom.parentNode.parentNode.parentNode
19609          // they get detached?
19610     }
19611     
19612     
19613     Roo.View.superclass.constructor.call(this);
19614     
19615     
19616 };
19617
19618 Roo.extend(Roo.View, Roo.util.Observable, {
19619     
19620      /**
19621      * @cfg {Roo.data.Store} store Data store to load data from.
19622      */
19623     store : false,
19624     
19625     /**
19626      * @cfg {String|Roo.Element} el The container element.
19627      */
19628     el : '',
19629     
19630     /**
19631      * @cfg {String|Roo.Template} tpl The template used by this View 
19632      */
19633     tpl : false,
19634     /**
19635      * @cfg {String} dataName the named area of the template to use as the data area
19636      *                          Works with domtemplates roo-name="name"
19637      */
19638     dataName: false,
19639     /**
19640      * @cfg {String} selectedClass The css class to add to selected nodes
19641      */
19642     selectedClass : "x-view-selected",
19643      /**
19644      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19645      */
19646     emptyText : "",
19647     
19648     /**
19649      * @cfg {String} text to display on mask (default Loading)
19650      */
19651     mask : false,
19652     /**
19653      * @cfg {Boolean} multiSelect Allow multiple selection
19654      */
19655     multiSelect : false,
19656     /**
19657      * @cfg {Boolean} singleSelect Allow single selection
19658      */
19659     singleSelect:  false,
19660     
19661     /**
19662      * @cfg {Boolean} toggleSelect - selecting 
19663      */
19664     toggleSelect : false,
19665     
19666     /**
19667      * @cfg {Boolean} tickable - selecting 
19668      */
19669     tickable : false,
19670     
19671     /**
19672      * Returns the element this view is bound to.
19673      * @return {Roo.Element}
19674      */
19675     getEl : function(){
19676         return this.wrapEl;
19677     },
19678     
19679     
19680
19681     /**
19682      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19683      */
19684     refresh : function(){
19685         //Roo.log('refresh');
19686         var t = this.tpl;
19687         
19688         // if we are using something like 'domtemplate', then
19689         // the what gets used is:
19690         // t.applySubtemplate(NAME, data, wrapping data..)
19691         // the outer template then get' applied with
19692         //     the store 'extra data'
19693         // and the body get's added to the
19694         //      roo-name="data" node?
19695         //      <span class='roo-tpl-{name}'></span> ?????
19696         
19697         
19698         
19699         this.clearSelections();
19700         this.el.update("");
19701         var html = [];
19702         var records = this.store.getRange();
19703         if(records.length < 1) {
19704             
19705             // is this valid??  = should it render a template??
19706             
19707             this.el.update(this.emptyText);
19708             return;
19709         }
19710         var el = this.el;
19711         if (this.dataName) {
19712             this.el.update(t.apply(this.store.meta)); //????
19713             el = this.el.child('.roo-tpl-' + this.dataName);
19714         }
19715         
19716         for(var i = 0, len = records.length; i < len; i++){
19717             var data = this.prepareData(records[i].data, i, records[i]);
19718             this.fireEvent("preparedata", this, data, i, records[i]);
19719             
19720             var d = Roo.apply({}, data);
19721             
19722             if(this.tickable){
19723                 Roo.apply(d, {'roo-id' : Roo.id()});
19724                 
19725                 var _this = this;
19726             
19727                 Roo.each(this.parent.item, function(item){
19728                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19729                         return;
19730                     }
19731                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19732                 });
19733             }
19734             
19735             html[html.length] = Roo.util.Format.trim(
19736                 this.dataName ?
19737                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19738                     t.apply(d)
19739             );
19740         }
19741         
19742         
19743         
19744         el.update(html.join(""));
19745         this.nodes = el.dom.childNodes;
19746         this.updateIndexes(0);
19747     },
19748     
19749
19750     /**
19751      * Function to override to reformat the data that is sent to
19752      * the template for each node.
19753      * DEPRICATED - use the preparedata event handler.
19754      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19755      * a JSON object for an UpdateManager bound view).
19756      */
19757     prepareData : function(data, index, record)
19758     {
19759         this.fireEvent("preparedata", this, data, index, record);
19760         return data;
19761     },
19762
19763     onUpdate : function(ds, record){
19764         // Roo.log('on update');   
19765         this.clearSelections();
19766         var index = this.store.indexOf(record);
19767         var n = this.nodes[index];
19768         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19769         n.parentNode.removeChild(n);
19770         this.updateIndexes(index, index);
19771     },
19772
19773     
19774     
19775 // --------- FIXME     
19776     onAdd : function(ds, records, index)
19777     {
19778         //Roo.log(['on Add', ds, records, index] );        
19779         this.clearSelections();
19780         if(this.nodes.length == 0){
19781             this.refresh();
19782             return;
19783         }
19784         var n = this.nodes[index];
19785         for(var i = 0, len = records.length; i < len; i++){
19786             var d = this.prepareData(records[i].data, i, records[i]);
19787             if(n){
19788                 this.tpl.insertBefore(n, d);
19789             }else{
19790                 
19791                 this.tpl.append(this.el, d);
19792             }
19793         }
19794         this.updateIndexes(index);
19795     },
19796
19797     onRemove : function(ds, record, index){
19798        // Roo.log('onRemove');
19799         this.clearSelections();
19800         var el = this.dataName  ?
19801             this.el.child('.roo-tpl-' + this.dataName) :
19802             this.el; 
19803         
19804         el.dom.removeChild(this.nodes[index]);
19805         this.updateIndexes(index);
19806     },
19807
19808     /**
19809      * Refresh an individual node.
19810      * @param {Number} index
19811      */
19812     refreshNode : function(index){
19813         this.onUpdate(this.store, this.store.getAt(index));
19814     },
19815
19816     updateIndexes : function(startIndex, endIndex){
19817         var ns = this.nodes;
19818         startIndex = startIndex || 0;
19819         endIndex = endIndex || ns.length - 1;
19820         for(var i = startIndex; i <= endIndex; i++){
19821             ns[i].nodeIndex = i;
19822         }
19823     },
19824
19825     /**
19826      * Changes the data store this view uses and refresh the view.
19827      * @param {Store} store
19828      */
19829     setStore : function(store, initial){
19830         if(!initial && this.store){
19831             this.store.un("datachanged", this.refresh);
19832             this.store.un("add", this.onAdd);
19833             this.store.un("remove", this.onRemove);
19834             this.store.un("update", this.onUpdate);
19835             this.store.un("clear", this.refresh);
19836             this.store.un("beforeload", this.onBeforeLoad);
19837             this.store.un("load", this.onLoad);
19838             this.store.un("loadexception", this.onLoad);
19839         }
19840         if(store){
19841           
19842             store.on("datachanged", this.refresh, this);
19843             store.on("add", this.onAdd, this);
19844             store.on("remove", this.onRemove, this);
19845             store.on("update", this.onUpdate, this);
19846             store.on("clear", this.refresh, this);
19847             store.on("beforeload", this.onBeforeLoad, this);
19848             store.on("load", this.onLoad, this);
19849             store.on("loadexception", this.onLoad, this);
19850         }
19851         
19852         if(store){
19853             this.refresh();
19854         }
19855     },
19856     /**
19857      * onbeforeLoad - masks the loading area.
19858      *
19859      */
19860     onBeforeLoad : function(store,opts)
19861     {
19862          //Roo.log('onBeforeLoad');   
19863         if (!opts.add) {
19864             this.el.update("");
19865         }
19866         this.el.mask(this.mask ? this.mask : "Loading" ); 
19867     },
19868     onLoad : function ()
19869     {
19870         this.el.unmask();
19871     },
19872     
19873
19874     /**
19875      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19876      * @param {HTMLElement} node
19877      * @return {HTMLElement} The template node
19878      */
19879     findItemFromChild : function(node){
19880         var el = this.dataName  ?
19881             this.el.child('.roo-tpl-' + this.dataName,true) :
19882             this.el.dom; 
19883         
19884         if(!node || node.parentNode == el){
19885                     return node;
19886             }
19887             var p = node.parentNode;
19888             while(p && p != el){
19889             if(p.parentNode == el){
19890                 return p;
19891             }
19892             p = p.parentNode;
19893         }
19894             return null;
19895     },
19896
19897     /** @ignore */
19898     onClick : function(e){
19899         var item = this.findItemFromChild(e.getTarget());
19900         if(item){
19901             var index = this.indexOf(item);
19902             if(this.onItemClick(item, index, e) !== false){
19903                 this.fireEvent("click", this, index, item, e);
19904             }
19905         }else{
19906             this.clearSelections();
19907         }
19908     },
19909
19910     /** @ignore */
19911     onContextMenu : function(e){
19912         var item = this.findItemFromChild(e.getTarget());
19913         if(item){
19914             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19915         }
19916     },
19917
19918     /** @ignore */
19919     onDblClick : function(e){
19920         var item = this.findItemFromChild(e.getTarget());
19921         if(item){
19922             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19923         }
19924     },
19925
19926     onItemClick : function(item, index, e)
19927     {
19928         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19929             return false;
19930         }
19931         if (this.toggleSelect) {
19932             var m = this.isSelected(item) ? 'unselect' : 'select';
19933             //Roo.log(m);
19934             var _t = this;
19935             _t[m](item, true, false);
19936             return true;
19937         }
19938         if(this.multiSelect || this.singleSelect){
19939             if(this.multiSelect && e.shiftKey && this.lastSelection){
19940                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19941             }else{
19942                 this.select(item, this.multiSelect && e.ctrlKey);
19943                 this.lastSelection = item;
19944             }
19945             
19946             if(!this.tickable){
19947                 e.preventDefault();
19948             }
19949             
19950         }
19951         return true;
19952     },
19953
19954     /**
19955      * Get the number of selected nodes.
19956      * @return {Number}
19957      */
19958     getSelectionCount : function(){
19959         return this.selections.length;
19960     },
19961
19962     /**
19963      * Get the currently selected nodes.
19964      * @return {Array} An array of HTMLElements
19965      */
19966     getSelectedNodes : function(){
19967         return this.selections;
19968     },
19969
19970     /**
19971      * Get the indexes of the selected nodes.
19972      * @return {Array}
19973      */
19974     getSelectedIndexes : function(){
19975         var indexes = [], s = this.selections;
19976         for(var i = 0, len = s.length; i < len; i++){
19977             indexes.push(s[i].nodeIndex);
19978         }
19979         return indexes;
19980     },
19981
19982     /**
19983      * Clear all selections
19984      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19985      */
19986     clearSelections : function(suppressEvent){
19987         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19988             this.cmp.elements = this.selections;
19989             this.cmp.removeClass(this.selectedClass);
19990             this.selections = [];
19991             if(!suppressEvent){
19992                 this.fireEvent("selectionchange", this, this.selections);
19993             }
19994         }
19995     },
19996
19997     /**
19998      * Returns true if the passed node is selected
19999      * @param {HTMLElement/Number} node The node or node index
20000      * @return {Boolean}
20001      */
20002     isSelected : function(node){
20003         var s = this.selections;
20004         if(s.length < 1){
20005             return false;
20006         }
20007         node = this.getNode(node);
20008         return s.indexOf(node) !== -1;
20009     },
20010
20011     /**
20012      * Selects nodes.
20013      * @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
20014      * @param {Boolean} keepExisting (optional) true to keep existing selections
20015      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20016      */
20017     select : function(nodeInfo, keepExisting, suppressEvent){
20018         if(nodeInfo instanceof Array){
20019             if(!keepExisting){
20020                 this.clearSelections(true);
20021             }
20022             for(var i = 0, len = nodeInfo.length; i < len; i++){
20023                 this.select(nodeInfo[i], true, true);
20024             }
20025             return;
20026         } 
20027         var node = this.getNode(nodeInfo);
20028         if(!node || this.isSelected(node)){
20029             return; // already selected.
20030         }
20031         if(!keepExisting){
20032             this.clearSelections(true);
20033         }
20034         
20035         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20036             Roo.fly(node).addClass(this.selectedClass);
20037             this.selections.push(node);
20038             if(!suppressEvent){
20039                 this.fireEvent("selectionchange", this, this.selections);
20040             }
20041         }
20042         
20043         
20044     },
20045       /**
20046      * Unselects nodes.
20047      * @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
20048      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20049      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20050      */
20051     unselect : function(nodeInfo, keepExisting, suppressEvent)
20052     {
20053         if(nodeInfo instanceof Array){
20054             Roo.each(this.selections, function(s) {
20055                 this.unselect(s, nodeInfo);
20056             }, this);
20057             return;
20058         }
20059         var node = this.getNode(nodeInfo);
20060         if(!node || !this.isSelected(node)){
20061             //Roo.log("not selected");
20062             return; // not selected.
20063         }
20064         // fireevent???
20065         var ns = [];
20066         Roo.each(this.selections, function(s) {
20067             if (s == node ) {
20068                 Roo.fly(node).removeClass(this.selectedClass);
20069
20070                 return;
20071             }
20072             ns.push(s);
20073         },this);
20074         
20075         this.selections= ns;
20076         this.fireEvent("selectionchange", this, this.selections);
20077     },
20078
20079     /**
20080      * Gets a template node.
20081      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20082      * @return {HTMLElement} The node or null if it wasn't found
20083      */
20084     getNode : function(nodeInfo){
20085         if(typeof nodeInfo == "string"){
20086             return document.getElementById(nodeInfo);
20087         }else if(typeof nodeInfo == "number"){
20088             return this.nodes[nodeInfo];
20089         }
20090         return nodeInfo;
20091     },
20092
20093     /**
20094      * Gets a range template nodes.
20095      * @param {Number} startIndex
20096      * @param {Number} endIndex
20097      * @return {Array} An array of nodes
20098      */
20099     getNodes : function(start, end){
20100         var ns = this.nodes;
20101         start = start || 0;
20102         end = typeof end == "undefined" ? ns.length - 1 : end;
20103         var nodes = [];
20104         if(start <= end){
20105             for(var i = start; i <= end; i++){
20106                 nodes.push(ns[i]);
20107             }
20108         } else{
20109             for(var i = start; i >= end; i--){
20110                 nodes.push(ns[i]);
20111             }
20112         }
20113         return nodes;
20114     },
20115
20116     /**
20117      * Finds the index of the passed node
20118      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20119      * @return {Number} The index of the node or -1
20120      */
20121     indexOf : function(node){
20122         node = this.getNode(node);
20123         if(typeof node.nodeIndex == "number"){
20124             return node.nodeIndex;
20125         }
20126         var ns = this.nodes;
20127         for(var i = 0, len = ns.length; i < len; i++){
20128             if(ns[i] == node){
20129                 return i;
20130             }
20131         }
20132         return -1;
20133     }
20134 });
20135 /*
20136  * - LGPL
20137  *
20138  * based on jquery fullcalendar
20139  * 
20140  */
20141
20142 Roo.bootstrap = Roo.bootstrap || {};
20143 /**
20144  * @class Roo.bootstrap.Calendar
20145  * @extends Roo.bootstrap.Component
20146  * Bootstrap Calendar class
20147  * @cfg {Boolean} loadMask (true|false) default false
20148  * @cfg {Object} header generate the user specific header of the calendar, default false
20149
20150  * @constructor
20151  * Create a new Container
20152  * @param {Object} config The config object
20153  */
20154
20155
20156
20157 Roo.bootstrap.Calendar = function(config){
20158     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20159      this.addEvents({
20160         /**
20161              * @event select
20162              * Fires when a date is selected
20163              * @param {DatePicker} this
20164              * @param {Date} date The selected date
20165              */
20166         'select': true,
20167         /**
20168              * @event monthchange
20169              * Fires when the displayed month changes 
20170              * @param {DatePicker} this
20171              * @param {Date} date The selected month
20172              */
20173         'monthchange': true,
20174         /**
20175              * @event evententer
20176              * Fires when mouse over an event
20177              * @param {Calendar} this
20178              * @param {event} Event
20179              */
20180         'evententer': true,
20181         /**
20182              * @event eventleave
20183              * Fires when the mouse leaves an
20184              * @param {Calendar} this
20185              * @param {event}
20186              */
20187         'eventleave': true,
20188         /**
20189              * @event eventclick
20190              * Fires when the mouse click an
20191              * @param {Calendar} this
20192              * @param {event}
20193              */
20194         'eventclick': true
20195         
20196     });
20197
20198 };
20199
20200 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20201     
20202      /**
20203      * @cfg {Number} startDay
20204      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20205      */
20206     startDay : 0,
20207     
20208     loadMask : false,
20209     
20210     header : false,
20211       
20212     getAutoCreate : function(){
20213         
20214         
20215         var fc_button = function(name, corner, style, content ) {
20216             return Roo.apply({},{
20217                 tag : 'span',
20218                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20219                          (corner.length ?
20220                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20221                             ''
20222                         ),
20223                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20224                 unselectable: 'on'
20225             });
20226         };
20227         
20228         var header = {};
20229         
20230         if(!this.header){
20231             header = {
20232                 tag : 'table',
20233                 cls : 'fc-header',
20234                 style : 'width:100%',
20235                 cn : [
20236                     {
20237                         tag: 'tr',
20238                         cn : [
20239                             {
20240                                 tag : 'td',
20241                                 cls : 'fc-header-left',
20242                                 cn : [
20243                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20244                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20245                                     { tag: 'span', cls: 'fc-header-space' },
20246                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20247
20248
20249                                 ]
20250                             },
20251
20252                             {
20253                                 tag : 'td',
20254                                 cls : 'fc-header-center',
20255                                 cn : [
20256                                     {
20257                                         tag: 'span',
20258                                         cls: 'fc-header-title',
20259                                         cn : {
20260                                             tag: 'H2',
20261                                             html : 'month / year'
20262                                         }
20263                                     }
20264
20265                                 ]
20266                             },
20267                             {
20268                                 tag : 'td',
20269                                 cls : 'fc-header-right',
20270                                 cn : [
20271                               /*      fc_button('month', 'left', '', 'month' ),
20272                                     fc_button('week', '', '', 'week' ),
20273                                     fc_button('day', 'right', '', 'day' )
20274                                 */    
20275
20276                                 ]
20277                             }
20278
20279                         ]
20280                     }
20281                 ]
20282             };
20283         }
20284         
20285         header = this.header;
20286         
20287        
20288         var cal_heads = function() {
20289             var ret = [];
20290             // fixme - handle this.
20291             
20292             for (var i =0; i < Date.dayNames.length; i++) {
20293                 var d = Date.dayNames[i];
20294                 ret.push({
20295                     tag: 'th',
20296                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20297                     html : d.substring(0,3)
20298                 });
20299                 
20300             }
20301             ret[0].cls += ' fc-first';
20302             ret[6].cls += ' fc-last';
20303             return ret;
20304         };
20305         var cal_cell = function(n) {
20306             return  {
20307                 tag: 'td',
20308                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20309                 cn : [
20310                     {
20311                         cn : [
20312                             {
20313                                 cls: 'fc-day-number',
20314                                 html: 'D'
20315                             },
20316                             {
20317                                 cls: 'fc-day-content',
20318                              
20319                                 cn : [
20320                                      {
20321                                         style: 'position: relative;' // height: 17px;
20322                                     }
20323                                 ]
20324                             }
20325                             
20326                             
20327                         ]
20328                     }
20329                 ]
20330                 
20331             }
20332         };
20333         var cal_rows = function() {
20334             
20335             var ret = [];
20336             for (var r = 0; r < 6; r++) {
20337                 var row= {
20338                     tag : 'tr',
20339                     cls : 'fc-week',
20340                     cn : []
20341                 };
20342                 
20343                 for (var i =0; i < Date.dayNames.length; i++) {
20344                     var d = Date.dayNames[i];
20345                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20346
20347                 }
20348                 row.cn[0].cls+=' fc-first';
20349                 row.cn[0].cn[0].style = 'min-height:90px';
20350                 row.cn[6].cls+=' fc-last';
20351                 ret.push(row);
20352                 
20353             }
20354             ret[0].cls += ' fc-first';
20355             ret[4].cls += ' fc-prev-last';
20356             ret[5].cls += ' fc-last';
20357             return ret;
20358             
20359         };
20360         
20361         var cal_table = {
20362             tag: 'table',
20363             cls: 'fc-border-separate',
20364             style : 'width:100%',
20365             cellspacing  : 0,
20366             cn : [
20367                 { 
20368                     tag: 'thead',
20369                     cn : [
20370                         { 
20371                             tag: 'tr',
20372                             cls : 'fc-first fc-last',
20373                             cn : cal_heads()
20374                         }
20375                     ]
20376                 },
20377                 { 
20378                     tag: 'tbody',
20379                     cn : cal_rows()
20380                 }
20381                   
20382             ]
20383         };
20384          
20385          var cfg = {
20386             cls : 'fc fc-ltr',
20387             cn : [
20388                 header,
20389                 {
20390                     cls : 'fc-content',
20391                     style : "position: relative;",
20392                     cn : [
20393                         {
20394                             cls : 'fc-view fc-view-month fc-grid',
20395                             style : 'position: relative',
20396                             unselectable : 'on',
20397                             cn : [
20398                                 {
20399                                     cls : 'fc-event-container',
20400                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20401                                 },
20402                                 cal_table
20403                             ]
20404                         }
20405                     ]
20406     
20407                 }
20408            ] 
20409             
20410         };
20411         
20412          
20413         
20414         return cfg;
20415     },
20416     
20417     
20418     initEvents : function()
20419     {
20420         if(!this.store){
20421             throw "can not find store for calendar";
20422         }
20423         
20424         var mark = {
20425             tag: "div",
20426             cls:"x-dlg-mask",
20427             style: "text-align:center",
20428             cn: [
20429                 {
20430                     tag: "div",
20431                     style: "background-color:white;width:50%;margin:250 auto",
20432                     cn: [
20433                         {
20434                             tag: "img",
20435                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20436                         },
20437                         {
20438                             tag: "span",
20439                             html: "Loading"
20440                         }
20441                         
20442                     ]
20443                 }
20444             ]
20445         };
20446         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20447         
20448         var size = this.el.select('.fc-content', true).first().getSize();
20449         this.maskEl.setSize(size.width, size.height);
20450         this.maskEl.enableDisplayMode("block");
20451         if(!this.loadMask){
20452             this.maskEl.hide();
20453         }
20454         
20455         this.store = Roo.factory(this.store, Roo.data);
20456         this.store.on('load', this.onLoad, this);
20457         this.store.on('beforeload', this.onBeforeLoad, this);
20458         
20459         this.resize();
20460         
20461         this.cells = this.el.select('.fc-day',true);
20462         //Roo.log(this.cells);
20463         this.textNodes = this.el.query('.fc-day-number');
20464         this.cells.addClassOnOver('fc-state-hover');
20465         
20466         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20467         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20468         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20469         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20470         
20471         this.on('monthchange', this.onMonthChange, this);
20472         
20473         this.update(new Date().clearTime());
20474     },
20475     
20476     resize : function() {
20477         var sz  = this.el.getSize();
20478         
20479         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20480         this.el.select('.fc-day-content div',true).setHeight(34);
20481     },
20482     
20483     
20484     // private
20485     showPrevMonth : function(e){
20486         this.update(this.activeDate.add("mo", -1));
20487     },
20488     showToday : function(e){
20489         this.update(new Date().clearTime());
20490     },
20491     // private
20492     showNextMonth : function(e){
20493         this.update(this.activeDate.add("mo", 1));
20494     },
20495
20496     // private
20497     showPrevYear : function(){
20498         this.update(this.activeDate.add("y", -1));
20499     },
20500
20501     // private
20502     showNextYear : function(){
20503         this.update(this.activeDate.add("y", 1));
20504     },
20505
20506     
20507    // private
20508     update : function(date)
20509     {
20510         var vd = this.activeDate;
20511         this.activeDate = date;
20512 //        if(vd && this.el){
20513 //            var t = date.getTime();
20514 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20515 //                Roo.log('using add remove');
20516 //                
20517 //                this.fireEvent('monthchange', this, date);
20518 //                
20519 //                this.cells.removeClass("fc-state-highlight");
20520 //                this.cells.each(function(c){
20521 //                   if(c.dateValue == t){
20522 //                       c.addClass("fc-state-highlight");
20523 //                       setTimeout(function(){
20524 //                            try{c.dom.firstChild.focus();}catch(e){}
20525 //                       }, 50);
20526 //                       return false;
20527 //                   }
20528 //                   return true;
20529 //                });
20530 //                return;
20531 //            }
20532 //        }
20533         
20534         var days = date.getDaysInMonth();
20535         
20536         var firstOfMonth = date.getFirstDateOfMonth();
20537         var startingPos = firstOfMonth.getDay()-this.startDay;
20538         
20539         if(startingPos < this.startDay){
20540             startingPos += 7;
20541         }
20542         
20543         var pm = date.add(Date.MONTH, -1);
20544         var prevStart = pm.getDaysInMonth()-startingPos;
20545 //        
20546         this.cells = this.el.select('.fc-day',true);
20547         this.textNodes = this.el.query('.fc-day-number');
20548         this.cells.addClassOnOver('fc-state-hover');
20549         
20550         var cells = this.cells.elements;
20551         var textEls = this.textNodes;
20552         
20553         Roo.each(cells, function(cell){
20554             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20555         });
20556         
20557         days += startingPos;
20558
20559         // convert everything to numbers so it's fast
20560         var day = 86400000;
20561         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20562         //Roo.log(d);
20563         //Roo.log(pm);
20564         //Roo.log(prevStart);
20565         
20566         var today = new Date().clearTime().getTime();
20567         var sel = date.clearTime().getTime();
20568         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20569         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20570         var ddMatch = this.disabledDatesRE;
20571         var ddText = this.disabledDatesText;
20572         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20573         var ddaysText = this.disabledDaysText;
20574         var format = this.format;
20575         
20576         var setCellClass = function(cal, cell){
20577             cell.row = 0;
20578             cell.events = [];
20579             cell.more = [];
20580             //Roo.log('set Cell Class');
20581             cell.title = "";
20582             var t = d.getTime();
20583             
20584             //Roo.log(d);
20585             
20586             cell.dateValue = t;
20587             if(t == today){
20588                 cell.className += " fc-today";
20589                 cell.className += " fc-state-highlight";
20590                 cell.title = cal.todayText;
20591             }
20592             if(t == sel){
20593                 // disable highlight in other month..
20594                 //cell.className += " fc-state-highlight";
20595                 
20596             }
20597             // disabling
20598             if(t < min) {
20599                 cell.className = " fc-state-disabled";
20600                 cell.title = cal.minText;
20601                 return;
20602             }
20603             if(t > max) {
20604                 cell.className = " fc-state-disabled";
20605                 cell.title = cal.maxText;
20606                 return;
20607             }
20608             if(ddays){
20609                 if(ddays.indexOf(d.getDay()) != -1){
20610                     cell.title = ddaysText;
20611                     cell.className = " fc-state-disabled";
20612                 }
20613             }
20614             if(ddMatch && format){
20615                 var fvalue = d.dateFormat(format);
20616                 if(ddMatch.test(fvalue)){
20617                     cell.title = ddText.replace("%0", fvalue);
20618                     cell.className = " fc-state-disabled";
20619                 }
20620             }
20621             
20622             if (!cell.initialClassName) {
20623                 cell.initialClassName = cell.dom.className;
20624             }
20625             
20626             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20627         };
20628
20629         var i = 0;
20630         
20631         for(; i < startingPos; i++) {
20632             textEls[i].innerHTML = (++prevStart);
20633             d.setDate(d.getDate()+1);
20634             
20635             cells[i].className = "fc-past fc-other-month";
20636             setCellClass(this, cells[i]);
20637         }
20638         
20639         var intDay = 0;
20640         
20641         for(; i < days; i++){
20642             intDay = i - startingPos + 1;
20643             textEls[i].innerHTML = (intDay);
20644             d.setDate(d.getDate()+1);
20645             
20646             cells[i].className = ''; // "x-date-active";
20647             setCellClass(this, cells[i]);
20648         }
20649         var extraDays = 0;
20650         
20651         for(; i < 42; i++) {
20652             textEls[i].innerHTML = (++extraDays);
20653             d.setDate(d.getDate()+1);
20654             
20655             cells[i].className = "fc-future fc-other-month";
20656             setCellClass(this, cells[i]);
20657         }
20658         
20659         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20660         
20661         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20662         
20663         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20664         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20665         
20666         if(totalRows != 6){
20667             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20668             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20669         }
20670         
20671         this.fireEvent('monthchange', this, date);
20672         
20673         
20674         /*
20675         if(!this.internalRender){
20676             var main = this.el.dom.firstChild;
20677             var w = main.offsetWidth;
20678             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20679             Roo.fly(main).setWidth(w);
20680             this.internalRender = true;
20681             // opera does not respect the auto grow header center column
20682             // then, after it gets a width opera refuses to recalculate
20683             // without a second pass
20684             if(Roo.isOpera && !this.secondPass){
20685                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20686                 this.secondPass = true;
20687                 this.update.defer(10, this, [date]);
20688             }
20689         }
20690         */
20691         
20692     },
20693     
20694     findCell : function(dt) {
20695         dt = dt.clearTime().getTime();
20696         var ret = false;
20697         this.cells.each(function(c){
20698             //Roo.log("check " +c.dateValue + '?=' + dt);
20699             if(c.dateValue == dt){
20700                 ret = c;
20701                 return false;
20702             }
20703             return true;
20704         });
20705         
20706         return ret;
20707     },
20708     
20709     findCells : function(ev) {
20710         var s = ev.start.clone().clearTime().getTime();
20711        // Roo.log(s);
20712         var e= ev.end.clone().clearTime().getTime();
20713        // Roo.log(e);
20714         var ret = [];
20715         this.cells.each(function(c){
20716              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20717             
20718             if(c.dateValue > e){
20719                 return ;
20720             }
20721             if(c.dateValue < s){
20722                 return ;
20723             }
20724             ret.push(c);
20725         });
20726         
20727         return ret;    
20728     },
20729     
20730 //    findBestRow: function(cells)
20731 //    {
20732 //        var ret = 0;
20733 //        
20734 //        for (var i =0 ; i < cells.length;i++) {
20735 //            ret  = Math.max(cells[i].rows || 0,ret);
20736 //        }
20737 //        return ret;
20738 //        
20739 //    },
20740     
20741     
20742     addItem : function(ev)
20743     {
20744         // look for vertical location slot in
20745         var cells = this.findCells(ev);
20746         
20747 //        ev.row = this.findBestRow(cells);
20748         
20749         // work out the location.
20750         
20751         var crow = false;
20752         var rows = [];
20753         for(var i =0; i < cells.length; i++) {
20754             
20755             cells[i].row = cells[0].row;
20756             
20757             if(i == 0){
20758                 cells[i].row = cells[i].row + 1;
20759             }
20760             
20761             if (!crow) {
20762                 crow = {
20763                     start : cells[i],
20764                     end :  cells[i]
20765                 };
20766                 continue;
20767             }
20768             if (crow.start.getY() == cells[i].getY()) {
20769                 // on same row.
20770                 crow.end = cells[i];
20771                 continue;
20772             }
20773             // different row.
20774             rows.push(crow);
20775             crow = {
20776                 start: cells[i],
20777                 end : cells[i]
20778             };
20779             
20780         }
20781         
20782         rows.push(crow);
20783         ev.els = [];
20784         ev.rows = rows;
20785         ev.cells = cells;
20786         
20787         cells[0].events.push(ev);
20788         
20789         this.calevents.push(ev);
20790     },
20791     
20792     clearEvents: function() {
20793         
20794         if(!this.calevents){
20795             return;
20796         }
20797         
20798         Roo.each(this.cells.elements, function(c){
20799             c.row = 0;
20800             c.events = [];
20801             c.more = [];
20802         });
20803         
20804         Roo.each(this.calevents, function(e) {
20805             Roo.each(e.els, function(el) {
20806                 el.un('mouseenter' ,this.onEventEnter, this);
20807                 el.un('mouseleave' ,this.onEventLeave, this);
20808                 el.remove();
20809             },this);
20810         },this);
20811         
20812         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20813             e.remove();
20814         });
20815         
20816     },
20817     
20818     renderEvents: function()
20819     {   
20820         var _this = this;
20821         
20822         this.cells.each(function(c) {
20823             
20824             if(c.row < 5){
20825                 return;
20826             }
20827             
20828             var ev = c.events;
20829             
20830             var r = 4;
20831             if(c.row != c.events.length){
20832                 r = 4 - (4 - (c.row - c.events.length));
20833             }
20834             
20835             c.events = ev.slice(0, r);
20836             c.more = ev.slice(r);
20837             
20838             if(c.more.length && c.more.length == 1){
20839                 c.events.push(c.more.pop());
20840             }
20841             
20842             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20843             
20844         });
20845             
20846         this.cells.each(function(c) {
20847             
20848             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20849             
20850             
20851             for (var e = 0; e < c.events.length; e++){
20852                 var ev = c.events[e];
20853                 var rows = ev.rows;
20854                 
20855                 for(var i = 0; i < rows.length; i++) {
20856                 
20857                     // how many rows should it span..
20858
20859                     var  cfg = {
20860                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20861                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20862
20863                         unselectable : "on",
20864                         cn : [
20865                             {
20866                                 cls: 'fc-event-inner',
20867                                 cn : [
20868     //                                {
20869     //                                  tag:'span',
20870     //                                  cls: 'fc-event-time',
20871     //                                  html : cells.length > 1 ? '' : ev.time
20872     //                                },
20873                                     {
20874                                       tag:'span',
20875                                       cls: 'fc-event-title',
20876                                       html : String.format('{0}', ev.title)
20877                                     }
20878
20879
20880                                 ]
20881                             },
20882                             {
20883                                 cls: 'ui-resizable-handle ui-resizable-e',
20884                                 html : '&nbsp;&nbsp;&nbsp'
20885                             }
20886
20887                         ]
20888                     };
20889
20890                     if (i == 0) {
20891                         cfg.cls += ' fc-event-start';
20892                     }
20893                     if ((i+1) == rows.length) {
20894                         cfg.cls += ' fc-event-end';
20895                     }
20896
20897                     var ctr = _this.el.select('.fc-event-container',true).first();
20898                     var cg = ctr.createChild(cfg);
20899
20900                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20901                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20902
20903                     var r = (c.more.length) ? 1 : 0;
20904                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20905                     cg.setWidth(ebox.right - sbox.x -2);
20906
20907                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20908                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20909                     cg.on('click', _this.onEventClick, _this, ev);
20910
20911                     ev.els.push(cg);
20912                     
20913                 }
20914                 
20915             }
20916             
20917             
20918             if(c.more.length){
20919                 var  cfg = {
20920                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20921                     style : 'position: absolute',
20922                     unselectable : "on",
20923                     cn : [
20924                         {
20925                             cls: 'fc-event-inner',
20926                             cn : [
20927                                 {
20928                                   tag:'span',
20929                                   cls: 'fc-event-title',
20930                                   html : 'More'
20931                                 }
20932
20933
20934                             ]
20935                         },
20936                         {
20937                             cls: 'ui-resizable-handle ui-resizable-e',
20938                             html : '&nbsp;&nbsp;&nbsp'
20939                         }
20940
20941                     ]
20942                 };
20943
20944                 var ctr = _this.el.select('.fc-event-container',true).first();
20945                 var cg = ctr.createChild(cfg);
20946
20947                 var sbox = c.select('.fc-day-content',true).first().getBox();
20948                 var ebox = c.select('.fc-day-content',true).first().getBox();
20949                 //Roo.log(cg);
20950                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20951                 cg.setWidth(ebox.right - sbox.x -2);
20952
20953                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20954                 
20955             }
20956             
20957         });
20958         
20959         
20960         
20961     },
20962     
20963     onEventEnter: function (e, el,event,d) {
20964         this.fireEvent('evententer', this, el, event);
20965     },
20966     
20967     onEventLeave: function (e, el,event,d) {
20968         this.fireEvent('eventleave', this, el, event);
20969     },
20970     
20971     onEventClick: function (e, el,event,d) {
20972         this.fireEvent('eventclick', this, el, event);
20973     },
20974     
20975     onMonthChange: function () {
20976         this.store.load();
20977     },
20978     
20979     onMoreEventClick: function(e, el, more)
20980     {
20981         var _this = this;
20982         
20983         this.calpopover.placement = 'right';
20984         this.calpopover.setTitle('More');
20985         
20986         this.calpopover.setContent('');
20987         
20988         var ctr = this.calpopover.el.select('.popover-content', true).first();
20989         
20990         Roo.each(more, function(m){
20991             var cfg = {
20992                 cls : 'fc-event-hori fc-event-draggable',
20993                 html : m.title
20994             };
20995             var cg = ctr.createChild(cfg);
20996             
20997             cg.on('click', _this.onEventClick, _this, m);
20998         });
20999         
21000         this.calpopover.show(el);
21001         
21002         
21003     },
21004     
21005     onLoad: function () 
21006     {   
21007         this.calevents = [];
21008         var cal = this;
21009         
21010         if(this.store.getCount() > 0){
21011             this.store.data.each(function(d){
21012                cal.addItem({
21013                     id : d.data.id,
21014                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21015                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21016                     time : d.data.start_time,
21017                     title : d.data.title,
21018                     description : d.data.description,
21019                     venue : d.data.venue
21020                 });
21021             });
21022         }
21023         
21024         this.renderEvents();
21025         
21026         if(this.calevents.length && this.loadMask){
21027             this.maskEl.hide();
21028         }
21029     },
21030     
21031     onBeforeLoad: function()
21032     {
21033         this.clearEvents();
21034         if(this.loadMask){
21035             this.maskEl.show();
21036         }
21037     }
21038 });
21039
21040  
21041  /*
21042  * - LGPL
21043  *
21044  * element
21045  * 
21046  */
21047
21048 /**
21049  * @class Roo.bootstrap.Popover
21050  * @extends Roo.bootstrap.Component
21051  * Bootstrap Popover class
21052  * @cfg {String} html contents of the popover   (or false to use children..)
21053  * @cfg {String} title of popover (or false to hide)
21054  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21055  * @cfg {String} trigger click || hover (or false to trigger manually)
21056  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21057  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21058  *      - if false and it has a 'parent' then it will be automatically added to that element
21059  *      - if string - Roo.get  will be called 
21060  * @cfg {Number} delay - delay before showing
21061  
21062  * @constructor
21063  * Create a new Popover
21064  * @param {Object} config The config object
21065  */
21066
21067 Roo.bootstrap.Popover = function(config){
21068     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21069     
21070     this.addEvents({
21071         // raw events
21072          /**
21073          * @event show
21074          * After the popover show
21075          * 
21076          * @param {Roo.bootstrap.Popover} this
21077          */
21078         "show" : true,
21079         /**
21080          * @event hide
21081          * After the popover hide
21082          * 
21083          * @param {Roo.bootstrap.Popover} this
21084          */
21085         "hide" : true
21086     });
21087 };
21088
21089 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21090     
21091     title: false,
21092     html: false,
21093     
21094     placement : 'right',
21095     trigger : 'hover', // hover
21096     modal : false,
21097     delay : 0,
21098     
21099     over: false,
21100     
21101     can_build_overlaid : false,
21102     
21103     maskEl : false, // the mask element
21104     headerEl : false,
21105     contentEl : false,
21106     alignEl : false, // when show is called with an element - this get's stored.
21107     
21108     getChildContainer : function()
21109     {
21110         return this.contentEl;
21111         
21112     },
21113     getPopoverHeader : function()
21114     {
21115         this.title = true; // flag not to hide it..
21116         this.headerEl.addClass('p-0');
21117         return this.headerEl
21118     },
21119     
21120     
21121     getAutoCreate : function(){
21122          
21123         var cfg = {
21124            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21125            style: 'display:block',
21126            cn : [
21127                 {
21128                     cls : 'arrow'
21129                 },
21130                 {
21131                     cls : 'popover-inner ',
21132                     cn : [
21133                         {
21134                             tag: 'h3',
21135                             cls: 'popover-title popover-header',
21136                             html : this.title === false ? '' : this.title
21137                         },
21138                         {
21139                             cls : 'popover-content popover-body '  + (this.cls || ''),
21140                             html : this.html || ''
21141                         }
21142                     ]
21143                     
21144                 }
21145            ]
21146         };
21147         
21148         return cfg;
21149     },
21150     /**
21151      * @param {string} the title
21152      */
21153     setTitle: function(str)
21154     {
21155         this.title = str;
21156         if (this.el) {
21157             this.headerEl.dom.innerHTML = str;
21158         }
21159         
21160     },
21161     /**
21162      * @param {string} the body content
21163      */
21164     setContent: function(str)
21165     {
21166         this.html = str;
21167         if (this.contentEl) {
21168             this.contentEl.dom.innerHTML = str;
21169         }
21170         
21171     },
21172     // as it get's added to the bottom of the page.
21173     onRender : function(ct, position)
21174     {
21175         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21176         
21177         
21178         
21179         if(!this.el){
21180             var cfg = Roo.apply({},  this.getAutoCreate());
21181             cfg.id = Roo.id();
21182             
21183             if (this.cls) {
21184                 cfg.cls += ' ' + this.cls;
21185             }
21186             if (this.style) {
21187                 cfg.style = this.style;
21188             }
21189             //Roo.log("adding to ");
21190             this.el = Roo.get(document.body).createChild(cfg, position);
21191 //            Roo.log(this.el);
21192         }
21193         
21194         this.contentEl = this.el.select('.popover-content',true).first();
21195         this.headerEl =  this.el.select('.popover-title',true).first();
21196         
21197         var nitems = [];
21198         if(typeof(this.items) != 'undefined'){
21199             var items = this.items;
21200             delete this.items;
21201
21202             for(var i =0;i < items.length;i++) {
21203                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21204             }
21205         }
21206
21207         this.items = nitems;
21208         
21209         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21210         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21211         
21212         
21213         
21214         this.initEvents();
21215     },
21216     
21217     resizeMask : function()
21218     {
21219         this.maskEl.setSize(
21220             Roo.lib.Dom.getViewWidth(true),
21221             Roo.lib.Dom.getViewHeight(true)
21222         );
21223     },
21224     
21225     initEvents : function()
21226     {
21227         
21228         if (!this.modal) { 
21229             Roo.bootstrap.Popover.register(this);
21230         }
21231          
21232         this.arrowEl = this.el.select('.arrow',true).first();
21233         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21234         this.el.enableDisplayMode('block');
21235         this.el.hide();
21236  
21237         
21238         if (this.over === false && !this.parent()) {
21239             return; 
21240         }
21241         if (this.triggers === false) {
21242             return;
21243         }
21244          
21245         // support parent
21246         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21247         var triggers = this.trigger ? this.trigger.split(' ') : [];
21248         Roo.each(triggers, function(trigger) {
21249         
21250             if (trigger == 'click') {
21251                 on_el.on('click', this.toggle, this);
21252             } else if (trigger != 'manual') {
21253                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21254                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21255       
21256                 on_el.on(eventIn  ,this.enter, this);
21257                 on_el.on(eventOut, this.leave, this);
21258             }
21259         }, this);
21260     },
21261     
21262     
21263     // private
21264     timeout : null,
21265     hoverState : null,
21266     
21267     toggle : function () {
21268         this.hoverState == 'in' ? this.leave() : this.enter();
21269     },
21270     
21271     enter : function () {
21272         
21273         clearTimeout(this.timeout);
21274     
21275         this.hoverState = 'in';
21276     
21277         if (!this.delay || !this.delay.show) {
21278             this.show();
21279             return;
21280         }
21281         var _t = this;
21282         this.timeout = setTimeout(function () {
21283             if (_t.hoverState == 'in') {
21284                 _t.show();
21285             }
21286         }, this.delay.show)
21287     },
21288     
21289     leave : function() {
21290         clearTimeout(this.timeout);
21291     
21292         this.hoverState = 'out';
21293     
21294         if (!this.delay || !this.delay.hide) {
21295             this.hide();
21296             return;
21297         }
21298         var _t = this;
21299         this.timeout = setTimeout(function () {
21300             if (_t.hoverState == 'out') {
21301                 _t.hide();
21302             }
21303         }, this.delay.hide)
21304     },
21305     /**
21306      * Show the popover
21307      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21308      * @param {string} (left|right|top|bottom) position
21309      */
21310     show : function (on_el, placement)
21311     {
21312         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21313         on_el = on_el || false; // default to false
21314          
21315         if (!on_el) {
21316             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21317                 on_el = this.parent().el;
21318             } else if (this.over) {
21319                 on_el = Roo.get(this.over);
21320             }
21321             
21322         }
21323         
21324         this.alignEl = Roo.get( on_el );
21325
21326         if (!this.el) {
21327             this.render(document.body);
21328         }
21329         
21330         
21331          
21332         
21333         if (this.title === false) {
21334             this.headerEl.hide();
21335         }
21336         
21337        
21338         this.el.show();
21339         this.el.dom.style.display = 'block';
21340          
21341  
21342         if (this.alignEl) {
21343             this.updatePosition(this.placement, true);
21344              
21345         } else {
21346             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21347             var es = this.el.getSize();
21348             var x = Roo.lib.Dom.getViewWidth()/2;
21349             var y = Roo.lib.Dom.getViewHeight()/2;
21350             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21351             
21352         }
21353
21354         
21355         //var arrow = this.el.select('.arrow',true).first();
21356         //arrow.set(align[2], 
21357         
21358         this.el.addClass('in');
21359         
21360          
21361         
21362         this.hoverState = 'in';
21363         
21364         if (this.modal) {
21365             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21366             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21367             this.maskEl.dom.style.display = 'block';
21368             this.maskEl.addClass('show');
21369         }
21370         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21371  
21372         this.fireEvent('show', this);
21373         
21374     },
21375     /**
21376      * fire this manually after loading a grid in the table for example
21377      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21378      * @param {Boolean} try and move it if we cant get right position.
21379      */
21380     updatePosition : function(placement, try_move)
21381     {
21382         // allow for calling with no parameters
21383         placement = placement   ? placement :  this.placement;
21384         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21385         
21386         this.el.removeClass([
21387             'fade','top','bottom', 'left', 'right','in',
21388             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21389         ]);
21390         this.el.addClass(placement + ' bs-popover-' + placement);
21391         
21392         if (!this.alignEl ) {
21393             return false;
21394         }
21395         
21396         switch (placement) {
21397             case 'right':
21398                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21399                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21400                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21401                     //normal display... or moved up/down.
21402                     this.el.setXY(offset);
21403                     var xy = this.alignEl.getAnchorXY('tr', false);
21404                     xy[0]+=2;xy[1]+=5;
21405                     this.arrowEl.setXY(xy);
21406                     return true;
21407                 }
21408                 // continue through...
21409                 return this.updatePosition('left', false);
21410                 
21411             
21412             case 'left':
21413                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21414                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21415                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21416                     //normal display... or moved up/down.
21417                     this.el.setXY(offset);
21418                     var xy = this.alignEl.getAnchorXY('tl', false);
21419                     xy[0]-=10;xy[1]+=5; // << fix me
21420                     this.arrowEl.setXY(xy);
21421                     return true;
21422                 }
21423                 // call self...
21424                 return this.updatePosition('right', false);
21425             
21426             case 'top':
21427                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21428                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21429                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21430                     //normal display... or moved up/down.
21431                     this.el.setXY(offset);
21432                     var xy = this.alignEl.getAnchorXY('t', false);
21433                     xy[1]-=10; // << fix me
21434                     this.arrowEl.setXY(xy);
21435                     return true;
21436                 }
21437                 // fall through
21438                return this.updatePosition('bottom', false);
21439             
21440             case 'bottom':
21441                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21442                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21443                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21444                     //normal display... or moved up/down.
21445                     this.el.setXY(offset);
21446                     var xy = this.alignEl.getAnchorXY('b', false);
21447                      xy[1]+=2; // << fix me
21448                     this.arrowEl.setXY(xy);
21449                     return true;
21450                 }
21451                 // fall through
21452                 return this.updatePosition('top', false);
21453                 
21454             
21455         }
21456         
21457         
21458         return false;
21459     },
21460     
21461     hide : function()
21462     {
21463         this.el.setXY([0,0]);
21464         this.el.removeClass('in');
21465         this.el.hide();
21466         this.hoverState = null;
21467         this.maskEl.hide(); // always..
21468         this.fireEvent('hide', this);
21469     }
21470     
21471 });
21472
21473
21474 Roo.apply(Roo.bootstrap.Popover, {
21475
21476     alignment : {
21477         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21478         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21479         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21480         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21481     },
21482     
21483     zIndex : 20001,
21484
21485     clickHander : false,
21486     
21487     
21488
21489     onMouseDown : function(e)
21490     {
21491         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21492             /// what is nothing is showing..
21493             this.hideAll();
21494         }
21495          
21496     },
21497     
21498     
21499     popups : [],
21500     
21501     register : function(popup)
21502     {
21503         if (!Roo.bootstrap.Popover.clickHandler) {
21504             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21505         }
21506         // hide other popups.
21507         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21508         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21509         this.hideAll(); //<< why?
21510         //this.popups.push(popup);
21511     },
21512     hideAll : function()
21513     {
21514         this.popups.forEach(function(p) {
21515             p.hide();
21516         });
21517     },
21518     onShow : function() {
21519         Roo.bootstrap.Popover.popups.push(this);
21520     },
21521     onHide : function() {
21522         Roo.bootstrap.Popover.popups.remove(this);
21523     } 
21524
21525 });/*
21526  * - LGPL
21527  *
21528  * Card header - holder for the card header elements.
21529  * 
21530  */
21531
21532 /**
21533  * @class Roo.bootstrap.PopoverNav
21534  * @extends Roo.bootstrap.NavGroup
21535  * Bootstrap Popover header navigation class
21536  * @constructor
21537  * Create a new Popover Header Navigation 
21538  * @param {Object} config The config object
21539  */
21540
21541 Roo.bootstrap.PopoverNav = function(config){
21542     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21543 };
21544
21545 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21546     
21547     
21548     container_method : 'getPopoverHeader' 
21549     
21550      
21551     
21552     
21553    
21554 });
21555
21556  
21557
21558  /*
21559  * - LGPL
21560  *
21561  * Progress
21562  * 
21563  */
21564
21565 /**
21566  * @class Roo.bootstrap.Progress
21567  * @extends Roo.bootstrap.Component
21568  * Bootstrap Progress class
21569  * @cfg {Boolean} striped striped of the progress bar
21570  * @cfg {Boolean} active animated of the progress bar
21571  * 
21572  * 
21573  * @constructor
21574  * Create a new Progress
21575  * @param {Object} config The config object
21576  */
21577
21578 Roo.bootstrap.Progress = function(config){
21579     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21583     
21584     striped : false,
21585     active: false,
21586     
21587     getAutoCreate : function(){
21588         var cfg = {
21589             tag: 'div',
21590             cls: 'progress'
21591         };
21592         
21593         
21594         if(this.striped){
21595             cfg.cls += ' progress-striped';
21596         }
21597       
21598         if(this.active){
21599             cfg.cls += ' active';
21600         }
21601         
21602         
21603         return cfg;
21604     }
21605    
21606 });
21607
21608  
21609
21610  /*
21611  * - LGPL
21612  *
21613  * ProgressBar
21614  * 
21615  */
21616
21617 /**
21618  * @class Roo.bootstrap.ProgressBar
21619  * @extends Roo.bootstrap.Component
21620  * Bootstrap ProgressBar class
21621  * @cfg {Number} aria_valuenow aria-value now
21622  * @cfg {Number} aria_valuemin aria-value min
21623  * @cfg {Number} aria_valuemax aria-value max
21624  * @cfg {String} label label for the progress bar
21625  * @cfg {String} panel (success | info | warning | danger )
21626  * @cfg {String} role role of the progress bar
21627  * @cfg {String} sr_only text
21628  * 
21629  * 
21630  * @constructor
21631  * Create a new ProgressBar
21632  * @param {Object} config The config object
21633  */
21634
21635 Roo.bootstrap.ProgressBar = function(config){
21636     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21637 };
21638
21639 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21640     
21641     aria_valuenow : 0,
21642     aria_valuemin : 0,
21643     aria_valuemax : 100,
21644     label : false,
21645     panel : false,
21646     role : false,
21647     sr_only: false,
21648     
21649     getAutoCreate : function()
21650     {
21651         
21652         var cfg = {
21653             tag: 'div',
21654             cls: 'progress-bar',
21655             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21656         };
21657         
21658         if(this.sr_only){
21659             cfg.cn = {
21660                 tag: 'span',
21661                 cls: 'sr-only',
21662                 html: this.sr_only
21663             }
21664         }
21665         
21666         if(this.role){
21667             cfg.role = this.role;
21668         }
21669         
21670         if(this.aria_valuenow){
21671             cfg['aria-valuenow'] = this.aria_valuenow;
21672         }
21673         
21674         if(this.aria_valuemin){
21675             cfg['aria-valuemin'] = this.aria_valuemin;
21676         }
21677         
21678         if(this.aria_valuemax){
21679             cfg['aria-valuemax'] = this.aria_valuemax;
21680         }
21681         
21682         if(this.label && !this.sr_only){
21683             cfg.html = this.label;
21684         }
21685         
21686         if(this.panel){
21687             cfg.cls += ' progress-bar-' + this.panel;
21688         }
21689         
21690         return cfg;
21691     },
21692     
21693     update : function(aria_valuenow)
21694     {
21695         this.aria_valuenow = aria_valuenow;
21696         
21697         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21698     }
21699    
21700 });
21701
21702  
21703
21704  /*
21705  * - LGPL
21706  *
21707  * column
21708  * 
21709  */
21710
21711 /**
21712  * @class Roo.bootstrap.TabGroup
21713  * @extends Roo.bootstrap.Column
21714  * Bootstrap Column class
21715  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21716  * @cfg {Boolean} carousel true to make the group behave like a carousel
21717  * @cfg {Boolean} bullets show bullets for the panels
21718  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21719  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21720  * @cfg {Boolean} showarrow (true|false) show arrow default true
21721  * 
21722  * @constructor
21723  * Create a new TabGroup
21724  * @param {Object} config The config object
21725  */
21726
21727 Roo.bootstrap.TabGroup = function(config){
21728     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21729     if (!this.navId) {
21730         this.navId = Roo.id();
21731     }
21732     this.tabs = [];
21733     Roo.bootstrap.TabGroup.register(this);
21734     
21735 };
21736
21737 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21738     
21739     carousel : false,
21740     transition : false,
21741     bullets : 0,
21742     timer : 0,
21743     autoslide : false,
21744     slideFn : false,
21745     slideOnTouch : false,
21746     showarrow : true,
21747     
21748     getAutoCreate : function()
21749     {
21750         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21751         
21752         cfg.cls += ' tab-content';
21753         
21754         if (this.carousel) {
21755             cfg.cls += ' carousel slide';
21756             
21757             cfg.cn = [{
21758                cls : 'carousel-inner',
21759                cn : []
21760             }];
21761         
21762             if(this.bullets  && !Roo.isTouch){
21763                 
21764                 var bullets = {
21765                     cls : 'carousel-bullets',
21766                     cn : []
21767                 };
21768                
21769                 if(this.bullets_cls){
21770                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21771                 }
21772                 
21773                 bullets.cn.push({
21774                     cls : 'clear'
21775                 });
21776                 
21777                 cfg.cn[0].cn.push(bullets);
21778             }
21779             
21780             if(this.showarrow){
21781                 cfg.cn[0].cn.push({
21782                     tag : 'div',
21783                     class : 'carousel-arrow',
21784                     cn : [
21785                         {
21786                             tag : 'div',
21787                             class : 'carousel-prev',
21788                             cn : [
21789                                 {
21790                                     tag : 'i',
21791                                     class : 'fa fa-chevron-left'
21792                                 }
21793                             ]
21794                         },
21795                         {
21796                             tag : 'div',
21797                             class : 'carousel-next',
21798                             cn : [
21799                                 {
21800                                     tag : 'i',
21801                                     class : 'fa fa-chevron-right'
21802                                 }
21803                             ]
21804                         }
21805                     ]
21806                 });
21807             }
21808             
21809         }
21810         
21811         return cfg;
21812     },
21813     
21814     initEvents:  function()
21815     {
21816 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21817 //            this.el.on("touchstart", this.onTouchStart, this);
21818 //        }
21819         
21820         if(this.autoslide){
21821             var _this = this;
21822             
21823             this.slideFn = window.setInterval(function() {
21824                 _this.showPanelNext();
21825             }, this.timer);
21826         }
21827         
21828         if(this.showarrow){
21829             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21830             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21831         }
21832         
21833         
21834     },
21835     
21836 //    onTouchStart : function(e, el, o)
21837 //    {
21838 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21839 //            return;
21840 //        }
21841 //        
21842 //        this.showPanelNext();
21843 //    },
21844     
21845     
21846     getChildContainer : function()
21847     {
21848         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21849     },
21850     
21851     /**
21852     * register a Navigation item
21853     * @param {Roo.bootstrap.NavItem} the navitem to add
21854     */
21855     register : function(item)
21856     {
21857         this.tabs.push( item);
21858         item.navId = this.navId; // not really needed..
21859         this.addBullet();
21860     
21861     },
21862     
21863     getActivePanel : function()
21864     {
21865         var r = false;
21866         Roo.each(this.tabs, function(t) {
21867             if (t.active) {
21868                 r = t;
21869                 return false;
21870             }
21871             return null;
21872         });
21873         return r;
21874         
21875     },
21876     getPanelByName : function(n)
21877     {
21878         var r = false;
21879         Roo.each(this.tabs, function(t) {
21880             if (t.tabId == n) {
21881                 r = t;
21882                 return false;
21883             }
21884             return null;
21885         });
21886         return r;
21887     },
21888     indexOfPanel : function(p)
21889     {
21890         var r = false;
21891         Roo.each(this.tabs, function(t,i) {
21892             if (t.tabId == p.tabId) {
21893                 r = i;
21894                 return false;
21895             }
21896             return null;
21897         });
21898         return r;
21899     },
21900     /**
21901      * show a specific panel
21902      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21903      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21904      */
21905     showPanel : function (pan)
21906     {
21907         if(this.transition || typeof(pan) == 'undefined'){
21908             Roo.log("waiting for the transitionend");
21909             return false;
21910         }
21911         
21912         if (typeof(pan) == 'number') {
21913             pan = this.tabs[pan];
21914         }
21915         
21916         if (typeof(pan) == 'string') {
21917             pan = this.getPanelByName(pan);
21918         }
21919         
21920         var cur = this.getActivePanel();
21921         
21922         if(!pan || !cur){
21923             Roo.log('pan or acitve pan is undefined');
21924             return false;
21925         }
21926         
21927         if (pan.tabId == this.getActivePanel().tabId) {
21928             return true;
21929         }
21930         
21931         if (false === cur.fireEvent('beforedeactivate')) {
21932             return false;
21933         }
21934         
21935         if(this.bullets > 0 && !Roo.isTouch){
21936             this.setActiveBullet(this.indexOfPanel(pan));
21937         }
21938         
21939         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21940             
21941             //class="carousel-item carousel-item-next carousel-item-left"
21942             
21943             this.transition = true;
21944             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21945             var lr = dir == 'next' ? 'left' : 'right';
21946             pan.el.addClass(dir); // or prev
21947             pan.el.addClass('carousel-item-' + dir); // or prev
21948             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21949             cur.el.addClass(lr); // or right
21950             pan.el.addClass(lr);
21951             cur.el.addClass('carousel-item-' +lr); // or right
21952             pan.el.addClass('carousel-item-' +lr);
21953             
21954             
21955             var _this = this;
21956             cur.el.on('transitionend', function() {
21957                 Roo.log("trans end?");
21958                 
21959                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21960                 pan.setActive(true);
21961                 
21962                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21963                 cur.setActive(false);
21964                 
21965                 _this.transition = false;
21966                 
21967             }, this, { single:  true } );
21968             
21969             return true;
21970         }
21971         
21972         cur.setActive(false);
21973         pan.setActive(true);
21974         
21975         return true;
21976         
21977     },
21978     showPanelNext : function()
21979     {
21980         var i = this.indexOfPanel(this.getActivePanel());
21981         
21982         if (i >= this.tabs.length - 1 && !this.autoslide) {
21983             return;
21984         }
21985         
21986         if (i >= this.tabs.length - 1 && this.autoslide) {
21987             i = -1;
21988         }
21989         
21990         this.showPanel(this.tabs[i+1]);
21991     },
21992     
21993     showPanelPrev : function()
21994     {
21995         var i = this.indexOfPanel(this.getActivePanel());
21996         
21997         if (i  < 1 && !this.autoslide) {
21998             return;
21999         }
22000         
22001         if (i < 1 && this.autoslide) {
22002             i = this.tabs.length;
22003         }
22004         
22005         this.showPanel(this.tabs[i-1]);
22006     },
22007     
22008     
22009     addBullet: function()
22010     {
22011         if(!this.bullets || Roo.isTouch){
22012             return;
22013         }
22014         var ctr = this.el.select('.carousel-bullets',true).first();
22015         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22016         var bullet = ctr.createChild({
22017             cls : 'bullet bullet-' + i
22018         },ctr.dom.lastChild);
22019         
22020         
22021         var _this = this;
22022         
22023         bullet.on('click', (function(e, el, o, ii, t){
22024
22025             e.preventDefault();
22026
22027             this.showPanel(ii);
22028
22029             if(this.autoslide && this.slideFn){
22030                 clearInterval(this.slideFn);
22031                 this.slideFn = window.setInterval(function() {
22032                     _this.showPanelNext();
22033                 }, this.timer);
22034             }
22035
22036         }).createDelegate(this, [i, bullet], true));
22037                 
22038         
22039     },
22040      
22041     setActiveBullet : function(i)
22042     {
22043         if(Roo.isTouch){
22044             return;
22045         }
22046         
22047         Roo.each(this.el.select('.bullet', true).elements, function(el){
22048             el.removeClass('selected');
22049         });
22050
22051         var bullet = this.el.select('.bullet-' + i, true).first();
22052         
22053         if(!bullet){
22054             return;
22055         }
22056         
22057         bullet.addClass('selected');
22058     }
22059     
22060     
22061   
22062 });
22063
22064  
22065
22066  
22067  
22068 Roo.apply(Roo.bootstrap.TabGroup, {
22069     
22070     groups: {},
22071      /**
22072     * register a Navigation Group
22073     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22074     */
22075     register : function(navgrp)
22076     {
22077         this.groups[navgrp.navId] = navgrp;
22078         
22079     },
22080     /**
22081     * fetch a Navigation Group based on the navigation ID
22082     * if one does not exist , it will get created.
22083     * @param {string} the navgroup to add
22084     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22085     */
22086     get: function(navId) {
22087         if (typeof(this.groups[navId]) == 'undefined') {
22088             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22089         }
22090         return this.groups[navId] ;
22091     }
22092     
22093     
22094     
22095 });
22096
22097  /*
22098  * - LGPL
22099  *
22100  * TabPanel
22101  * 
22102  */
22103
22104 /**
22105  * @class Roo.bootstrap.TabPanel
22106  * @extends Roo.bootstrap.Component
22107  * Bootstrap TabPanel class
22108  * @cfg {Boolean} active panel active
22109  * @cfg {String} html panel content
22110  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22111  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22112  * @cfg {String} href click to link..
22113  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22114  * 
22115  * 
22116  * @constructor
22117  * Create a new TabPanel
22118  * @param {Object} config The config object
22119  */
22120
22121 Roo.bootstrap.TabPanel = function(config){
22122     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22123     this.addEvents({
22124         /**
22125              * @event changed
22126              * Fires when the active status changes
22127              * @param {Roo.bootstrap.TabPanel} this
22128              * @param {Boolean} state the new state
22129             
22130          */
22131         'changed': true,
22132         /**
22133              * @event beforedeactivate
22134              * Fires before a tab is de-activated - can be used to do validation on a form.
22135              * @param {Roo.bootstrap.TabPanel} this
22136              * @return {Boolean} false if there is an error
22137             
22138          */
22139         'beforedeactivate': true
22140      });
22141     
22142     this.tabId = this.tabId || Roo.id();
22143   
22144 };
22145
22146 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22147     
22148     active: false,
22149     html: false,
22150     tabId: false,
22151     navId : false,
22152     href : '',
22153     touchSlide : false,
22154     getAutoCreate : function(){
22155         
22156         
22157         var cfg = {
22158             tag: 'div',
22159             // item is needed for carousel - not sure if it has any effect otherwise
22160             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22161             html: this.html || ''
22162         };
22163         
22164         if(this.active){
22165             cfg.cls += ' active';
22166         }
22167         
22168         if(this.tabId){
22169             cfg.tabId = this.tabId;
22170         }
22171         
22172         
22173         
22174         return cfg;
22175     },
22176     
22177     initEvents:  function()
22178     {
22179         var p = this.parent();
22180         
22181         this.navId = this.navId || p.navId;
22182         
22183         if (typeof(this.navId) != 'undefined') {
22184             // not really needed.. but just in case.. parent should be a NavGroup.
22185             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22186             
22187             tg.register(this);
22188             
22189             var i = tg.tabs.length - 1;
22190             
22191             if(this.active && tg.bullets > 0 && i < tg.bullets){
22192                 tg.setActiveBullet(i);
22193             }
22194         }
22195         
22196         this.el.on('click', this.onClick, this);
22197         
22198         if(Roo.isTouch && this.touchSlide){
22199             this.el.on("touchstart", this.onTouchStart, this);
22200             this.el.on("touchmove", this.onTouchMove, this);
22201             this.el.on("touchend", this.onTouchEnd, this);
22202         }
22203         
22204     },
22205     
22206     onRender : function(ct, position)
22207     {
22208         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22209     },
22210     
22211     setActive : function(state)
22212     {
22213         Roo.log("panel - set active " + this.tabId + "=" + state);
22214         
22215         this.active = state;
22216         if (!state) {
22217             this.el.removeClass('active');
22218             
22219         } else  if (!this.el.hasClass('active')) {
22220             this.el.addClass('active');
22221         }
22222         
22223         this.fireEvent('changed', this, state);
22224     },
22225     
22226     onClick : function(e)
22227     {
22228         e.preventDefault();
22229         
22230         if(!this.href.length){
22231             return;
22232         }
22233         
22234         window.location.href = this.href;
22235     },
22236     
22237     startX : 0,
22238     startY : 0,
22239     endX : 0,
22240     endY : 0,
22241     swiping : false,
22242     
22243     onTouchStart : function(e)
22244     {
22245         this.swiping = false;
22246         
22247         this.startX = e.browserEvent.touches[0].clientX;
22248         this.startY = e.browserEvent.touches[0].clientY;
22249     },
22250     
22251     onTouchMove : function(e)
22252     {
22253         this.swiping = true;
22254         
22255         this.endX = e.browserEvent.touches[0].clientX;
22256         this.endY = e.browserEvent.touches[0].clientY;
22257     },
22258     
22259     onTouchEnd : function(e)
22260     {
22261         if(!this.swiping){
22262             this.onClick(e);
22263             return;
22264         }
22265         
22266         var tabGroup = this.parent();
22267         
22268         if(this.endX > this.startX){ // swiping right
22269             tabGroup.showPanelPrev();
22270             return;
22271         }
22272         
22273         if(this.startX > this.endX){ // swiping left
22274             tabGroup.showPanelNext();
22275             return;
22276         }
22277     }
22278     
22279     
22280 });
22281  
22282
22283  
22284
22285  /*
22286  * - LGPL
22287  *
22288  * DateField
22289  * 
22290  */
22291
22292 /**
22293  * @class Roo.bootstrap.DateField
22294  * @extends Roo.bootstrap.Input
22295  * Bootstrap DateField class
22296  * @cfg {Number} weekStart default 0
22297  * @cfg {String} viewMode default empty, (months|years)
22298  * @cfg {String} minViewMode default empty, (months|years)
22299  * @cfg {Number} startDate default -Infinity
22300  * @cfg {Number} endDate default Infinity
22301  * @cfg {Boolean} todayHighlight default false
22302  * @cfg {Boolean} todayBtn default false
22303  * @cfg {Boolean} calendarWeeks default false
22304  * @cfg {Object} daysOfWeekDisabled default empty
22305  * @cfg {Boolean} singleMode default false (true | false)
22306  * 
22307  * @cfg {Boolean} keyboardNavigation default true
22308  * @cfg {String} language default en
22309  * 
22310  * @constructor
22311  * Create a new DateField
22312  * @param {Object} config The config object
22313  */
22314
22315 Roo.bootstrap.DateField = function(config){
22316     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22317      this.addEvents({
22318             /**
22319              * @event show
22320              * Fires when this field show.
22321              * @param {Roo.bootstrap.DateField} this
22322              * @param {Mixed} date The date value
22323              */
22324             show : true,
22325             /**
22326              * @event show
22327              * Fires when this field hide.
22328              * @param {Roo.bootstrap.DateField} this
22329              * @param {Mixed} date The date value
22330              */
22331             hide : true,
22332             /**
22333              * @event select
22334              * Fires when select a date.
22335              * @param {Roo.bootstrap.DateField} this
22336              * @param {Mixed} date The date value
22337              */
22338             select : true,
22339             /**
22340              * @event beforeselect
22341              * Fires when before select a date.
22342              * @param {Roo.bootstrap.DateField} this
22343              * @param {Mixed} date The date value
22344              */
22345             beforeselect : true
22346         });
22347 };
22348
22349 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22350     
22351     /**
22352      * @cfg {String} format
22353      * The default date format string which can be overriden for localization support.  The format must be
22354      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22355      */
22356     format : "m/d/y",
22357     /**
22358      * @cfg {String} altFormats
22359      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22360      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22361      */
22362     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22363     
22364     weekStart : 0,
22365     
22366     viewMode : '',
22367     
22368     minViewMode : '',
22369     
22370     todayHighlight : false,
22371     
22372     todayBtn: false,
22373     
22374     language: 'en',
22375     
22376     keyboardNavigation: true,
22377     
22378     calendarWeeks: false,
22379     
22380     startDate: -Infinity,
22381     
22382     endDate: Infinity,
22383     
22384     daysOfWeekDisabled: [],
22385     
22386     _events: [],
22387     
22388     singleMode : false,
22389     
22390     UTCDate: function()
22391     {
22392         return new Date(Date.UTC.apply(Date, arguments));
22393     },
22394     
22395     UTCToday: function()
22396     {
22397         var today = new Date();
22398         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22399     },
22400     
22401     getDate: function() {
22402             var d = this.getUTCDate();
22403             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22404     },
22405     
22406     getUTCDate: function() {
22407             return this.date;
22408     },
22409     
22410     setDate: function(d) {
22411             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22412     },
22413     
22414     setUTCDate: function(d) {
22415             this.date = d;
22416             this.setValue(this.formatDate(this.date));
22417     },
22418         
22419     onRender: function(ct, position)
22420     {
22421         
22422         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22423         
22424         this.language = this.language || 'en';
22425         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22426         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22427         
22428         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22429         this.format = this.format || 'm/d/y';
22430         this.isInline = false;
22431         this.isInput = true;
22432         this.component = this.el.select('.add-on', true).first() || false;
22433         this.component = (this.component && this.component.length === 0) ? false : this.component;
22434         this.hasInput = this.component && this.inputEl().length;
22435         
22436         if (typeof(this.minViewMode === 'string')) {
22437             switch (this.minViewMode) {
22438                 case 'months':
22439                     this.minViewMode = 1;
22440                     break;
22441                 case 'years':
22442                     this.minViewMode = 2;
22443                     break;
22444                 default:
22445                     this.minViewMode = 0;
22446                     break;
22447             }
22448         }
22449         
22450         if (typeof(this.viewMode === 'string')) {
22451             switch (this.viewMode) {
22452                 case 'months':
22453                     this.viewMode = 1;
22454                     break;
22455                 case 'years':
22456                     this.viewMode = 2;
22457                     break;
22458                 default:
22459                     this.viewMode = 0;
22460                     break;
22461             }
22462         }
22463                 
22464         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22465         
22466 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22467         
22468         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22469         
22470         this.picker().on('mousedown', this.onMousedown, this);
22471         this.picker().on('click', this.onClick, this);
22472         
22473         this.picker().addClass('datepicker-dropdown');
22474         
22475         this.startViewMode = this.viewMode;
22476         
22477         if(this.singleMode){
22478             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22479                 v.setVisibilityMode(Roo.Element.DISPLAY);
22480                 v.hide();
22481             });
22482             
22483             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22484                 v.setStyle('width', '189px');
22485             });
22486         }
22487         
22488         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22489             if(!this.calendarWeeks){
22490                 v.remove();
22491                 return;
22492             }
22493             
22494             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22495             v.attr('colspan', function(i, val){
22496                 return parseInt(val) + 1;
22497             });
22498         });
22499                         
22500         
22501         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22502         
22503         this.setStartDate(this.startDate);
22504         this.setEndDate(this.endDate);
22505         
22506         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22507         
22508         this.fillDow();
22509         this.fillMonths();
22510         this.update();
22511         this.showMode();
22512         
22513         if(this.isInline) {
22514             this.showPopup();
22515         }
22516     },
22517     
22518     picker : function()
22519     {
22520         return this.pickerEl;
22521 //        return this.el.select('.datepicker', true).first();
22522     },
22523     
22524     fillDow: function()
22525     {
22526         var dowCnt = this.weekStart;
22527         
22528         var dow = {
22529             tag: 'tr',
22530             cn: [
22531                 
22532             ]
22533         };
22534         
22535         if(this.calendarWeeks){
22536             dow.cn.push({
22537                 tag: 'th',
22538                 cls: 'cw',
22539                 html: '&nbsp;'
22540             })
22541         }
22542         
22543         while (dowCnt < this.weekStart + 7) {
22544             dow.cn.push({
22545                 tag: 'th',
22546                 cls: 'dow',
22547                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22548             });
22549         }
22550         
22551         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22552     },
22553     
22554     fillMonths: function()
22555     {    
22556         var i = 0;
22557         var months = this.picker().select('>.datepicker-months td', true).first();
22558         
22559         months.dom.innerHTML = '';
22560         
22561         while (i < 12) {
22562             var month = {
22563                 tag: 'span',
22564                 cls: 'month',
22565                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22566             };
22567             
22568             months.createChild(month);
22569         }
22570         
22571     },
22572     
22573     update: function()
22574     {
22575         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;
22576         
22577         if (this.date < this.startDate) {
22578             this.viewDate = new Date(this.startDate);
22579         } else if (this.date > this.endDate) {
22580             this.viewDate = new Date(this.endDate);
22581         } else {
22582             this.viewDate = new Date(this.date);
22583         }
22584         
22585         this.fill();
22586     },
22587     
22588     fill: function() 
22589     {
22590         var d = new Date(this.viewDate),
22591                 year = d.getUTCFullYear(),
22592                 month = d.getUTCMonth(),
22593                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22594                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22595                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22596                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22597                 currentDate = this.date && this.date.valueOf(),
22598                 today = this.UTCToday();
22599         
22600         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22601         
22602 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22603         
22604 //        this.picker.select('>tfoot th.today').
22605 //                                              .text(dates[this.language].today)
22606 //                                              .toggle(this.todayBtn !== false);
22607     
22608         this.updateNavArrows();
22609         this.fillMonths();
22610                                                 
22611         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22612         
22613         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22614          
22615         prevMonth.setUTCDate(day);
22616         
22617         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22618         
22619         var nextMonth = new Date(prevMonth);
22620         
22621         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22622         
22623         nextMonth = nextMonth.valueOf();
22624         
22625         var fillMonths = false;
22626         
22627         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22628         
22629         while(prevMonth.valueOf() <= nextMonth) {
22630             var clsName = '';
22631             
22632             if (prevMonth.getUTCDay() === this.weekStart) {
22633                 if(fillMonths){
22634                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22635                 }
22636                     
22637                 fillMonths = {
22638                     tag: 'tr',
22639                     cn: []
22640                 };
22641                 
22642                 if(this.calendarWeeks){
22643                     // ISO 8601: First week contains first thursday.
22644                     // ISO also states week starts on Monday, but we can be more abstract here.
22645                     var
22646                     // Start of current week: based on weekstart/current date
22647                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22648                     // Thursday of this week
22649                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22650                     // First Thursday of year, year from thursday
22651                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22652                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22653                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22654                     
22655                     fillMonths.cn.push({
22656                         tag: 'td',
22657                         cls: 'cw',
22658                         html: calWeek
22659                     });
22660                 }
22661             }
22662             
22663             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22664                 clsName += ' old';
22665             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22666                 clsName += ' new';
22667             }
22668             if (this.todayHighlight &&
22669                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22670                 prevMonth.getUTCMonth() == today.getMonth() &&
22671                 prevMonth.getUTCDate() == today.getDate()) {
22672                 clsName += ' today';
22673             }
22674             
22675             if (currentDate && prevMonth.valueOf() === currentDate) {
22676                 clsName += ' active';
22677             }
22678             
22679             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22680                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22681                     clsName += ' disabled';
22682             }
22683             
22684             fillMonths.cn.push({
22685                 tag: 'td',
22686                 cls: 'day ' + clsName,
22687                 html: prevMonth.getDate()
22688             });
22689             
22690             prevMonth.setDate(prevMonth.getDate()+1);
22691         }
22692           
22693         var currentYear = this.date && this.date.getUTCFullYear();
22694         var currentMonth = this.date && this.date.getUTCMonth();
22695         
22696         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22697         
22698         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22699             v.removeClass('active');
22700             
22701             if(currentYear === year && k === currentMonth){
22702                 v.addClass('active');
22703             }
22704             
22705             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22706                 v.addClass('disabled');
22707             }
22708             
22709         });
22710         
22711         
22712         year = parseInt(year/10, 10) * 10;
22713         
22714         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22715         
22716         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22717         
22718         year -= 1;
22719         for (var i = -1; i < 11; i++) {
22720             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22721                 tag: 'span',
22722                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22723                 html: year
22724             });
22725             
22726             year += 1;
22727         }
22728     },
22729     
22730     showMode: function(dir) 
22731     {
22732         if (dir) {
22733             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22734         }
22735         
22736         Roo.each(this.picker().select('>div',true).elements, function(v){
22737             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22738             v.hide();
22739         });
22740         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22741     },
22742     
22743     place: function()
22744     {
22745         if(this.isInline) {
22746             return;
22747         }
22748         
22749         this.picker().removeClass(['bottom', 'top']);
22750         
22751         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22752             /*
22753              * place to the top of element!
22754              *
22755              */
22756             
22757             this.picker().addClass('top');
22758             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22759             
22760             return;
22761         }
22762         
22763         this.picker().addClass('bottom');
22764         
22765         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22766     },
22767     
22768     parseDate : function(value)
22769     {
22770         if(!value || value instanceof Date){
22771             return value;
22772         }
22773         var v = Date.parseDate(value, this.format);
22774         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22775             v = Date.parseDate(value, 'Y-m-d');
22776         }
22777         if(!v && this.altFormats){
22778             if(!this.altFormatsArray){
22779                 this.altFormatsArray = this.altFormats.split("|");
22780             }
22781             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22782                 v = Date.parseDate(value, this.altFormatsArray[i]);
22783             }
22784         }
22785         return v;
22786     },
22787     
22788     formatDate : function(date, fmt)
22789     {   
22790         return (!date || !(date instanceof Date)) ?
22791         date : date.dateFormat(fmt || this.format);
22792     },
22793     
22794     onFocus : function()
22795     {
22796         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22797         this.showPopup();
22798     },
22799     
22800     onBlur : function()
22801     {
22802         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22803         
22804         var d = this.inputEl().getValue();
22805         
22806         this.setValue(d);
22807                 
22808         this.hidePopup();
22809     },
22810     
22811     showPopup : function()
22812     {
22813         this.picker().show();
22814         this.update();
22815         this.place();
22816         
22817         this.fireEvent('showpopup', this, this.date);
22818     },
22819     
22820     hidePopup : function()
22821     {
22822         if(this.isInline) {
22823             return;
22824         }
22825         this.picker().hide();
22826         this.viewMode = this.startViewMode;
22827         this.showMode();
22828         
22829         this.fireEvent('hidepopup', this, this.date);
22830         
22831     },
22832     
22833     onMousedown: function(e)
22834     {
22835         e.stopPropagation();
22836         e.preventDefault();
22837     },
22838     
22839     keyup: function(e)
22840     {
22841         Roo.bootstrap.DateField.superclass.keyup.call(this);
22842         this.update();
22843     },
22844
22845     setValue: function(v)
22846     {
22847         if(this.fireEvent('beforeselect', this, v) !== false){
22848             var d = new Date(this.parseDate(v) ).clearTime();
22849         
22850             if(isNaN(d.getTime())){
22851                 this.date = this.viewDate = '';
22852                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22853                 return;
22854             }
22855
22856             v = this.formatDate(d);
22857
22858             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22859
22860             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22861
22862             this.update();
22863
22864             this.fireEvent('select', this, this.date);
22865         }
22866     },
22867     
22868     getValue: function()
22869     {
22870         return this.formatDate(this.date);
22871     },
22872     
22873     fireKey: function(e)
22874     {
22875         if (!this.picker().isVisible()){
22876             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22877                 this.showPopup();
22878             }
22879             return;
22880         }
22881         
22882         var dateChanged = false,
22883         dir, day, month,
22884         newDate, newViewDate;
22885         
22886         switch(e.keyCode){
22887             case 27: // escape
22888                 this.hidePopup();
22889                 e.preventDefault();
22890                 break;
22891             case 37: // left
22892             case 39: // right
22893                 if (!this.keyboardNavigation) {
22894                     break;
22895                 }
22896                 dir = e.keyCode == 37 ? -1 : 1;
22897                 
22898                 if (e.ctrlKey){
22899                     newDate = this.moveYear(this.date, dir);
22900                     newViewDate = this.moveYear(this.viewDate, dir);
22901                 } else if (e.shiftKey){
22902                     newDate = this.moveMonth(this.date, dir);
22903                     newViewDate = this.moveMonth(this.viewDate, dir);
22904                 } else {
22905                     newDate = new Date(this.date);
22906                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22907                     newViewDate = new Date(this.viewDate);
22908                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22909                 }
22910                 if (this.dateWithinRange(newDate)){
22911                     this.date = newDate;
22912                     this.viewDate = newViewDate;
22913                     this.setValue(this.formatDate(this.date));
22914 //                    this.update();
22915                     e.preventDefault();
22916                     dateChanged = true;
22917                 }
22918                 break;
22919             case 38: // up
22920             case 40: // down
22921                 if (!this.keyboardNavigation) {
22922                     break;
22923                 }
22924                 dir = e.keyCode == 38 ? -1 : 1;
22925                 if (e.ctrlKey){
22926                     newDate = this.moveYear(this.date, dir);
22927                     newViewDate = this.moveYear(this.viewDate, dir);
22928                 } else if (e.shiftKey){
22929                     newDate = this.moveMonth(this.date, dir);
22930                     newViewDate = this.moveMonth(this.viewDate, dir);
22931                 } else {
22932                     newDate = new Date(this.date);
22933                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22934                     newViewDate = new Date(this.viewDate);
22935                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22936                 }
22937                 if (this.dateWithinRange(newDate)){
22938                     this.date = newDate;
22939                     this.viewDate = newViewDate;
22940                     this.setValue(this.formatDate(this.date));
22941 //                    this.update();
22942                     e.preventDefault();
22943                     dateChanged = true;
22944                 }
22945                 break;
22946             case 13: // enter
22947                 this.setValue(this.formatDate(this.date));
22948                 this.hidePopup();
22949                 e.preventDefault();
22950                 break;
22951             case 9: // tab
22952                 this.setValue(this.formatDate(this.date));
22953                 this.hidePopup();
22954                 break;
22955             case 16: // shift
22956             case 17: // ctrl
22957             case 18: // alt
22958                 break;
22959             default :
22960                 this.hidePopup();
22961                 
22962         }
22963     },
22964     
22965     
22966     onClick: function(e) 
22967     {
22968         e.stopPropagation();
22969         e.preventDefault();
22970         
22971         var target = e.getTarget();
22972         
22973         if(target.nodeName.toLowerCase() === 'i'){
22974             target = Roo.get(target).dom.parentNode;
22975         }
22976         
22977         var nodeName = target.nodeName;
22978         var className = target.className;
22979         var html = target.innerHTML;
22980         //Roo.log(nodeName);
22981         
22982         switch(nodeName.toLowerCase()) {
22983             case 'th':
22984                 switch(className) {
22985                     case 'switch':
22986                         this.showMode(1);
22987                         break;
22988                     case 'prev':
22989                     case 'next':
22990                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22991                         switch(this.viewMode){
22992                                 case 0:
22993                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22994                                         break;
22995                                 case 1:
22996                                 case 2:
22997                                         this.viewDate = this.moveYear(this.viewDate, dir);
22998                                         break;
22999                         }
23000                         this.fill();
23001                         break;
23002                     case 'today':
23003                         var date = new Date();
23004                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23005 //                        this.fill()
23006                         this.setValue(this.formatDate(this.date));
23007                         
23008                         this.hidePopup();
23009                         break;
23010                 }
23011                 break;
23012             case 'span':
23013                 if (className.indexOf('disabled') < 0) {
23014                 if (!this.viewDate) {
23015                     this.viewDate = new Date();
23016                 }
23017                 this.viewDate.setUTCDate(1);
23018                     if (className.indexOf('month') > -1) {
23019                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23020                     } else {
23021                         var year = parseInt(html, 10) || 0;
23022                         this.viewDate.setUTCFullYear(year);
23023                         
23024                     }
23025                     
23026                     if(this.singleMode){
23027                         this.setValue(this.formatDate(this.viewDate));
23028                         this.hidePopup();
23029                         return;
23030                     }
23031                     
23032                     this.showMode(-1);
23033                     this.fill();
23034                 }
23035                 break;
23036                 
23037             case 'td':
23038                 //Roo.log(className);
23039                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23040                     var day = parseInt(html, 10) || 1;
23041                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23042                         month = (this.viewDate || new Date()).getUTCMonth();
23043
23044                     if (className.indexOf('old') > -1) {
23045                         if(month === 0 ){
23046                             month = 11;
23047                             year -= 1;
23048                         }else{
23049                             month -= 1;
23050                         }
23051                     } else if (className.indexOf('new') > -1) {
23052                         if (month == 11) {
23053                             month = 0;
23054                             year += 1;
23055                         } else {
23056                             month += 1;
23057                         }
23058                     }
23059                     //Roo.log([year,month,day]);
23060                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23061                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23062 //                    this.fill();
23063                     //Roo.log(this.formatDate(this.date));
23064                     this.setValue(this.formatDate(this.date));
23065                     this.hidePopup();
23066                 }
23067                 break;
23068         }
23069     },
23070     
23071     setStartDate: function(startDate)
23072     {
23073         this.startDate = startDate || -Infinity;
23074         if (this.startDate !== -Infinity) {
23075             this.startDate = this.parseDate(this.startDate);
23076         }
23077         this.update();
23078         this.updateNavArrows();
23079     },
23080
23081     setEndDate: function(endDate)
23082     {
23083         this.endDate = endDate || Infinity;
23084         if (this.endDate !== Infinity) {
23085             this.endDate = this.parseDate(this.endDate);
23086         }
23087         this.update();
23088         this.updateNavArrows();
23089     },
23090     
23091     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23092     {
23093         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23094         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23095             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23096         }
23097         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23098             return parseInt(d, 10);
23099         });
23100         this.update();
23101         this.updateNavArrows();
23102     },
23103     
23104     updateNavArrows: function() 
23105     {
23106         if(this.singleMode){
23107             return;
23108         }
23109         
23110         var d = new Date(this.viewDate),
23111         year = d.getUTCFullYear(),
23112         month = d.getUTCMonth();
23113         
23114         Roo.each(this.picker().select('.prev', true).elements, function(v){
23115             v.show();
23116             switch (this.viewMode) {
23117                 case 0:
23118
23119                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23120                         v.hide();
23121                     }
23122                     break;
23123                 case 1:
23124                 case 2:
23125                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23126                         v.hide();
23127                     }
23128                     break;
23129             }
23130         });
23131         
23132         Roo.each(this.picker().select('.next', true).elements, function(v){
23133             v.show();
23134             switch (this.viewMode) {
23135                 case 0:
23136
23137                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23138                         v.hide();
23139                     }
23140                     break;
23141                 case 1:
23142                 case 2:
23143                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23144                         v.hide();
23145                     }
23146                     break;
23147             }
23148         })
23149     },
23150     
23151     moveMonth: function(date, dir)
23152     {
23153         if (!dir) {
23154             return date;
23155         }
23156         var new_date = new Date(date.valueOf()),
23157         day = new_date.getUTCDate(),
23158         month = new_date.getUTCMonth(),
23159         mag = Math.abs(dir),
23160         new_month, test;
23161         dir = dir > 0 ? 1 : -1;
23162         if (mag == 1){
23163             test = dir == -1
23164             // If going back one month, make sure month is not current month
23165             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23166             ? function(){
23167                 return new_date.getUTCMonth() == month;
23168             }
23169             // If going forward one month, make sure month is as expected
23170             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23171             : function(){
23172                 return new_date.getUTCMonth() != new_month;
23173             };
23174             new_month = month + dir;
23175             new_date.setUTCMonth(new_month);
23176             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23177             if (new_month < 0 || new_month > 11) {
23178                 new_month = (new_month + 12) % 12;
23179             }
23180         } else {
23181             // For magnitudes >1, move one month at a time...
23182             for (var i=0; i<mag; i++) {
23183                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23184                 new_date = this.moveMonth(new_date, dir);
23185             }
23186             // ...then reset the day, keeping it in the new month
23187             new_month = new_date.getUTCMonth();
23188             new_date.setUTCDate(day);
23189             test = function(){
23190                 return new_month != new_date.getUTCMonth();
23191             };
23192         }
23193         // Common date-resetting loop -- if date is beyond end of month, make it
23194         // end of month
23195         while (test()){
23196             new_date.setUTCDate(--day);
23197             new_date.setUTCMonth(new_month);
23198         }
23199         return new_date;
23200     },
23201
23202     moveYear: function(date, dir)
23203     {
23204         return this.moveMonth(date, dir*12);
23205     },
23206
23207     dateWithinRange: function(date)
23208     {
23209         return date >= this.startDate && date <= this.endDate;
23210     },
23211
23212     
23213     remove: function() 
23214     {
23215         this.picker().remove();
23216     },
23217     
23218     validateValue : function(value)
23219     {
23220         if(this.getVisibilityEl().hasClass('hidden')){
23221             return true;
23222         }
23223         
23224         if(value.length < 1)  {
23225             if(this.allowBlank){
23226                 return true;
23227             }
23228             return false;
23229         }
23230         
23231         if(value.length < this.minLength){
23232             return false;
23233         }
23234         if(value.length > this.maxLength){
23235             return false;
23236         }
23237         if(this.vtype){
23238             var vt = Roo.form.VTypes;
23239             if(!vt[this.vtype](value, this)){
23240                 return false;
23241             }
23242         }
23243         if(typeof this.validator == "function"){
23244             var msg = this.validator(value);
23245             if(msg !== true){
23246                 return false;
23247             }
23248         }
23249         
23250         if(this.regex && !this.regex.test(value)){
23251             return false;
23252         }
23253         
23254         if(typeof(this.parseDate(value)) == 'undefined'){
23255             return false;
23256         }
23257         
23258         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23259             return false;
23260         }      
23261         
23262         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23263             return false;
23264         } 
23265         
23266         
23267         return true;
23268     },
23269     
23270     reset : function()
23271     {
23272         this.date = this.viewDate = '';
23273         
23274         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23275     }
23276    
23277 });
23278
23279 Roo.apply(Roo.bootstrap.DateField,  {
23280     
23281     head : {
23282         tag: 'thead',
23283         cn: [
23284         {
23285             tag: 'tr',
23286             cn: [
23287             {
23288                 tag: 'th',
23289                 cls: 'prev',
23290                 html: '<i class="fa fa-arrow-left"/>'
23291             },
23292             {
23293                 tag: 'th',
23294                 cls: 'switch',
23295                 colspan: '5'
23296             },
23297             {
23298                 tag: 'th',
23299                 cls: 'next',
23300                 html: '<i class="fa fa-arrow-right"/>'
23301             }
23302
23303             ]
23304         }
23305         ]
23306     },
23307     
23308     content : {
23309         tag: 'tbody',
23310         cn: [
23311         {
23312             tag: 'tr',
23313             cn: [
23314             {
23315                 tag: 'td',
23316                 colspan: '7'
23317             }
23318             ]
23319         }
23320         ]
23321     },
23322     
23323     footer : {
23324         tag: 'tfoot',
23325         cn: [
23326         {
23327             tag: 'tr',
23328             cn: [
23329             {
23330                 tag: 'th',
23331                 colspan: '7',
23332                 cls: 'today'
23333             }
23334                     
23335             ]
23336         }
23337         ]
23338     },
23339     
23340     dates:{
23341         en: {
23342             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23343             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23344             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23345             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23346             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23347             today: "Today"
23348         }
23349     },
23350     
23351     modes: [
23352     {
23353         clsName: 'days',
23354         navFnc: 'Month',
23355         navStep: 1
23356     },
23357     {
23358         clsName: 'months',
23359         navFnc: 'FullYear',
23360         navStep: 1
23361     },
23362     {
23363         clsName: 'years',
23364         navFnc: 'FullYear',
23365         navStep: 10
23366     }]
23367 });
23368
23369 Roo.apply(Roo.bootstrap.DateField,  {
23370   
23371     template : {
23372         tag: 'div',
23373         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23374         cn: [
23375         {
23376             tag: 'div',
23377             cls: 'datepicker-days',
23378             cn: [
23379             {
23380                 tag: 'table',
23381                 cls: 'table-condensed',
23382                 cn:[
23383                 Roo.bootstrap.DateField.head,
23384                 {
23385                     tag: 'tbody'
23386                 },
23387                 Roo.bootstrap.DateField.footer
23388                 ]
23389             }
23390             ]
23391         },
23392         {
23393             tag: 'div',
23394             cls: 'datepicker-months',
23395             cn: [
23396             {
23397                 tag: 'table',
23398                 cls: 'table-condensed',
23399                 cn:[
23400                 Roo.bootstrap.DateField.head,
23401                 Roo.bootstrap.DateField.content,
23402                 Roo.bootstrap.DateField.footer
23403                 ]
23404             }
23405             ]
23406         },
23407         {
23408             tag: 'div',
23409             cls: 'datepicker-years',
23410             cn: [
23411             {
23412                 tag: 'table',
23413                 cls: 'table-condensed',
23414                 cn:[
23415                 Roo.bootstrap.DateField.head,
23416                 Roo.bootstrap.DateField.content,
23417                 Roo.bootstrap.DateField.footer
23418                 ]
23419             }
23420             ]
23421         }
23422         ]
23423     }
23424 });
23425
23426  
23427
23428  /*
23429  * - LGPL
23430  *
23431  * TimeField
23432  * 
23433  */
23434
23435 /**
23436  * @class Roo.bootstrap.TimeField
23437  * @extends Roo.bootstrap.Input
23438  * Bootstrap DateField class
23439  * 
23440  * 
23441  * @constructor
23442  * Create a new TimeField
23443  * @param {Object} config The config object
23444  */
23445
23446 Roo.bootstrap.TimeField = function(config){
23447     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23448     this.addEvents({
23449             /**
23450              * @event show
23451              * Fires when this field show.
23452              * @param {Roo.bootstrap.DateField} thisthis
23453              * @param {Mixed} date The date value
23454              */
23455             show : true,
23456             /**
23457              * @event show
23458              * Fires when this field hide.
23459              * @param {Roo.bootstrap.DateField} this
23460              * @param {Mixed} date The date value
23461              */
23462             hide : true,
23463             /**
23464              * @event select
23465              * Fires when select a date.
23466              * @param {Roo.bootstrap.DateField} this
23467              * @param {Mixed} date The date value
23468              */
23469             select : true
23470         });
23471 };
23472
23473 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23474     
23475     /**
23476      * @cfg {String} format
23477      * The default time format string which can be overriden for localization support.  The format must be
23478      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23479      */
23480     format : "H:i",
23481
23482     getAutoCreate : function()
23483     {
23484         this.after = '<i class="fa far fa-clock"></i>';
23485         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23486         
23487          
23488     },
23489     onRender: function(ct, position)
23490     {
23491         
23492         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23493                 
23494         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23495         
23496         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23497         
23498         this.pop = this.picker().select('>.datepicker-time',true).first();
23499         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23500         
23501         this.picker().on('mousedown', this.onMousedown, this);
23502         this.picker().on('click', this.onClick, this);
23503         
23504         this.picker().addClass('datepicker-dropdown');
23505     
23506         this.fillTime();
23507         this.update();
23508             
23509         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23510         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23511         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23512         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23513         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23514         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23515
23516     },
23517     
23518     fireKey: function(e){
23519         if (!this.picker().isVisible()){
23520             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23521                 this.show();
23522             }
23523             return;
23524         }
23525
23526         e.preventDefault();
23527         
23528         switch(e.keyCode){
23529             case 27: // escape
23530                 this.hide();
23531                 break;
23532             case 37: // left
23533             case 39: // right
23534                 this.onTogglePeriod();
23535                 break;
23536             case 38: // up
23537                 this.onIncrementMinutes();
23538                 break;
23539             case 40: // down
23540                 this.onDecrementMinutes();
23541                 break;
23542             case 13: // enter
23543             case 9: // tab
23544                 this.setTime();
23545                 break;
23546         }
23547     },
23548     
23549     onClick: function(e) {
23550         e.stopPropagation();
23551         e.preventDefault();
23552     },
23553     
23554     picker : function()
23555     {
23556         return this.pickerEl;
23557     },
23558     
23559     fillTime: function()
23560     {    
23561         var time = this.pop.select('tbody', true).first();
23562         
23563         time.dom.innerHTML = '';
23564         
23565         time.createChild({
23566             tag: 'tr',
23567             cn: [
23568                 {
23569                     tag: 'td',
23570                     cn: [
23571                         {
23572                             tag: 'a',
23573                             href: '#',
23574                             cls: 'btn',
23575                             cn: [
23576                                 {
23577                                     tag: 'i',
23578                                     cls: 'hours-up fa fas fa-chevron-up'
23579                                 }
23580                             ]
23581                         } 
23582                     ]
23583                 },
23584                 {
23585                     tag: 'td',
23586                     cls: 'separator'
23587                 },
23588                 {
23589                     tag: 'td',
23590                     cn: [
23591                         {
23592                             tag: 'a',
23593                             href: '#',
23594                             cls: 'btn',
23595                             cn: [
23596                                 {
23597                                     tag: 'i',
23598                                     cls: 'minutes-up fa fas fa-chevron-up'
23599                                 }
23600                             ]
23601                         }
23602                     ]
23603                 },
23604                 {
23605                     tag: 'td',
23606                     cls: 'separator'
23607                 }
23608             ]
23609         });
23610         
23611         time.createChild({
23612             tag: 'tr',
23613             cn: [
23614                 {
23615                     tag: 'td',
23616                     cn: [
23617                         {
23618                             tag: 'span',
23619                             cls: 'timepicker-hour',
23620                             html: '00'
23621                         }  
23622                     ]
23623                 },
23624                 {
23625                     tag: 'td',
23626                     cls: 'separator',
23627                     html: ':'
23628                 },
23629                 {
23630                     tag: 'td',
23631                     cn: [
23632                         {
23633                             tag: 'span',
23634                             cls: 'timepicker-minute',
23635                             html: '00'
23636                         }  
23637                     ]
23638                 },
23639                 {
23640                     tag: 'td',
23641                     cls: 'separator'
23642                 },
23643                 {
23644                     tag: 'td',
23645                     cn: [
23646                         {
23647                             tag: 'button',
23648                             type: 'button',
23649                             cls: 'btn btn-primary period',
23650                             html: 'AM'
23651                             
23652                         }
23653                     ]
23654                 }
23655             ]
23656         });
23657         
23658         time.createChild({
23659             tag: 'tr',
23660             cn: [
23661                 {
23662                     tag: 'td',
23663                     cn: [
23664                         {
23665                             tag: 'a',
23666                             href: '#',
23667                             cls: 'btn',
23668                             cn: [
23669                                 {
23670                                     tag: 'span',
23671                                     cls: 'hours-down fa fas fa-chevron-down'
23672                                 }
23673                             ]
23674                         }
23675                     ]
23676                 },
23677                 {
23678                     tag: 'td',
23679                     cls: 'separator'
23680                 },
23681                 {
23682                     tag: 'td',
23683                     cn: [
23684                         {
23685                             tag: 'a',
23686                             href: '#',
23687                             cls: 'btn',
23688                             cn: [
23689                                 {
23690                                     tag: 'span',
23691                                     cls: 'minutes-down fa fas fa-chevron-down'
23692                                 }
23693                             ]
23694                         }
23695                     ]
23696                 },
23697                 {
23698                     tag: 'td',
23699                     cls: 'separator'
23700                 }
23701             ]
23702         });
23703         
23704     },
23705     
23706     update: function()
23707     {
23708         
23709         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23710         
23711         this.fill();
23712     },
23713     
23714     fill: function() 
23715     {
23716         var hours = this.time.getHours();
23717         var minutes = this.time.getMinutes();
23718         var period = 'AM';
23719         
23720         if(hours > 11){
23721             period = 'PM';
23722         }
23723         
23724         if(hours == 0){
23725             hours = 12;
23726         }
23727         
23728         
23729         if(hours > 12){
23730             hours = hours - 12;
23731         }
23732         
23733         if(hours < 10){
23734             hours = '0' + hours;
23735         }
23736         
23737         if(minutes < 10){
23738             minutes = '0' + minutes;
23739         }
23740         
23741         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23742         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23743         this.pop.select('button', true).first().dom.innerHTML = period;
23744         
23745     },
23746     
23747     place: function()
23748     {   
23749         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23750         
23751         var cls = ['bottom'];
23752         
23753         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23754             cls.pop();
23755             cls.push('top');
23756         }
23757         
23758         cls.push('right');
23759         
23760         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23761             cls.pop();
23762             cls.push('left');
23763         }
23764         //this.picker().setXY(20000,20000);
23765         this.picker().addClass(cls.join('-'));
23766         
23767         var _this = this;
23768         
23769         Roo.each(cls, function(c){
23770             if(c == 'bottom'){
23771                 (function() {
23772                  //  
23773                 }).defer(200);
23774                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23775                 //_this.picker().setTop(_this.inputEl().getHeight());
23776                 return;
23777             }
23778             if(c == 'top'){
23779                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23780                 
23781                 //_this.picker().setTop(0 - _this.picker().getHeight());
23782                 return;
23783             }
23784             /*
23785             if(c == 'left'){
23786                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23787                 return;
23788             }
23789             if(c == 'right'){
23790                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23791                 return;
23792             }
23793             */
23794         });
23795         
23796     },
23797   
23798     onFocus : function()
23799     {
23800         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23801         this.show();
23802     },
23803     
23804     onBlur : function()
23805     {
23806         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23807         this.hide();
23808     },
23809     
23810     show : function()
23811     {
23812         this.picker().show();
23813         this.pop.show();
23814         this.update();
23815         this.place();
23816         
23817         this.fireEvent('show', this, this.date);
23818     },
23819     
23820     hide : function()
23821     {
23822         this.picker().hide();
23823         this.pop.hide();
23824         
23825         this.fireEvent('hide', this, this.date);
23826     },
23827     
23828     setTime : function()
23829     {
23830         this.hide();
23831         this.setValue(this.time.format(this.format));
23832         
23833         this.fireEvent('select', this, this.date);
23834         
23835         
23836     },
23837     
23838     onMousedown: function(e){
23839         e.stopPropagation();
23840         e.preventDefault();
23841     },
23842     
23843     onIncrementHours: function()
23844     {
23845         Roo.log('onIncrementHours');
23846         this.time = this.time.add(Date.HOUR, 1);
23847         this.update();
23848         
23849     },
23850     
23851     onDecrementHours: function()
23852     {
23853         Roo.log('onDecrementHours');
23854         this.time = this.time.add(Date.HOUR, -1);
23855         this.update();
23856     },
23857     
23858     onIncrementMinutes: function()
23859     {
23860         Roo.log('onIncrementMinutes');
23861         this.time = this.time.add(Date.MINUTE, 1);
23862         this.update();
23863     },
23864     
23865     onDecrementMinutes: function()
23866     {
23867         Roo.log('onDecrementMinutes');
23868         this.time = this.time.add(Date.MINUTE, -1);
23869         this.update();
23870     },
23871     
23872     onTogglePeriod: function()
23873     {
23874         Roo.log('onTogglePeriod');
23875         this.time = this.time.add(Date.HOUR, 12);
23876         this.update();
23877     }
23878     
23879    
23880 });
23881  
23882
23883 Roo.apply(Roo.bootstrap.TimeField,  {
23884   
23885     template : {
23886         tag: 'div',
23887         cls: 'datepicker dropdown-menu',
23888         cn: [
23889             {
23890                 tag: 'div',
23891                 cls: 'datepicker-time',
23892                 cn: [
23893                 {
23894                     tag: 'table',
23895                     cls: 'table-condensed',
23896                     cn:[
23897                         {
23898                             tag: 'tbody',
23899                             cn: [
23900                                 {
23901                                     tag: 'tr',
23902                                     cn: [
23903                                     {
23904                                         tag: 'td',
23905                                         colspan: '7'
23906                                     }
23907                                     ]
23908                                 }
23909                             ]
23910                         },
23911                         {
23912                             tag: 'tfoot',
23913                             cn: [
23914                                 {
23915                                     tag: 'tr',
23916                                     cn: [
23917                                     {
23918                                         tag: 'th',
23919                                         colspan: '7',
23920                                         cls: '',
23921                                         cn: [
23922                                             {
23923                                                 tag: 'button',
23924                                                 cls: 'btn btn-info ok',
23925                                                 html: 'OK'
23926                                             }
23927                                         ]
23928                                     }
23929                     
23930                                     ]
23931                                 }
23932                             ]
23933                         }
23934                     ]
23935                 }
23936                 ]
23937             }
23938         ]
23939     }
23940 });
23941
23942  
23943
23944  /*
23945  * - LGPL
23946  *
23947  * MonthField
23948  * 
23949  */
23950
23951 /**
23952  * @class Roo.bootstrap.MonthField
23953  * @extends Roo.bootstrap.Input
23954  * Bootstrap MonthField class
23955  * 
23956  * @cfg {String} language default en
23957  * 
23958  * @constructor
23959  * Create a new MonthField
23960  * @param {Object} config The config object
23961  */
23962
23963 Roo.bootstrap.MonthField = function(config){
23964     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23965     
23966     this.addEvents({
23967         /**
23968          * @event show
23969          * Fires when this field show.
23970          * @param {Roo.bootstrap.MonthField} this
23971          * @param {Mixed} date The date value
23972          */
23973         show : true,
23974         /**
23975          * @event show
23976          * Fires when this field hide.
23977          * @param {Roo.bootstrap.MonthField} this
23978          * @param {Mixed} date The date value
23979          */
23980         hide : true,
23981         /**
23982          * @event select
23983          * Fires when select a date.
23984          * @param {Roo.bootstrap.MonthField} this
23985          * @param {String} oldvalue The old value
23986          * @param {String} newvalue The new value
23987          */
23988         select : true
23989     });
23990 };
23991
23992 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23993     
23994     onRender: function(ct, position)
23995     {
23996         
23997         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23998         
23999         this.language = this.language || 'en';
24000         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24001         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24002         
24003         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24004         this.isInline = false;
24005         this.isInput = true;
24006         this.component = this.el.select('.add-on', true).first() || false;
24007         this.component = (this.component && this.component.length === 0) ? false : this.component;
24008         this.hasInput = this.component && this.inputEL().length;
24009         
24010         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24011         
24012         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24013         
24014         this.picker().on('mousedown', this.onMousedown, this);
24015         this.picker().on('click', this.onClick, this);
24016         
24017         this.picker().addClass('datepicker-dropdown');
24018         
24019         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24020             v.setStyle('width', '189px');
24021         });
24022         
24023         this.fillMonths();
24024         
24025         this.update();
24026         
24027         if(this.isInline) {
24028             this.show();
24029         }
24030         
24031     },
24032     
24033     setValue: function(v, suppressEvent)
24034     {   
24035         var o = this.getValue();
24036         
24037         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24038         
24039         this.update();
24040
24041         if(suppressEvent !== true){
24042             this.fireEvent('select', this, o, v);
24043         }
24044         
24045     },
24046     
24047     getValue: function()
24048     {
24049         return this.value;
24050     },
24051     
24052     onClick: function(e) 
24053     {
24054         e.stopPropagation();
24055         e.preventDefault();
24056         
24057         var target = e.getTarget();
24058         
24059         if(target.nodeName.toLowerCase() === 'i'){
24060             target = Roo.get(target).dom.parentNode;
24061         }
24062         
24063         var nodeName = target.nodeName;
24064         var className = target.className;
24065         var html = target.innerHTML;
24066         
24067         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24068             return;
24069         }
24070         
24071         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24072         
24073         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24074         
24075         this.hide();
24076                         
24077     },
24078     
24079     picker : function()
24080     {
24081         return this.pickerEl;
24082     },
24083     
24084     fillMonths: function()
24085     {    
24086         var i = 0;
24087         var months = this.picker().select('>.datepicker-months td', true).first();
24088         
24089         months.dom.innerHTML = '';
24090         
24091         while (i < 12) {
24092             var month = {
24093                 tag: 'span',
24094                 cls: 'month',
24095                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24096             };
24097             
24098             months.createChild(month);
24099         }
24100         
24101     },
24102     
24103     update: function()
24104     {
24105         var _this = this;
24106         
24107         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24108             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24109         }
24110         
24111         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24112             e.removeClass('active');
24113             
24114             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24115                 e.addClass('active');
24116             }
24117         })
24118     },
24119     
24120     place: function()
24121     {
24122         if(this.isInline) {
24123             return;
24124         }
24125         
24126         this.picker().removeClass(['bottom', 'top']);
24127         
24128         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24129             /*
24130              * place to the top of element!
24131              *
24132              */
24133             
24134             this.picker().addClass('top');
24135             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24136             
24137             return;
24138         }
24139         
24140         this.picker().addClass('bottom');
24141         
24142         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24143     },
24144     
24145     onFocus : function()
24146     {
24147         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24148         this.show();
24149     },
24150     
24151     onBlur : function()
24152     {
24153         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24154         
24155         var d = this.inputEl().getValue();
24156         
24157         this.setValue(d);
24158                 
24159         this.hide();
24160     },
24161     
24162     show : function()
24163     {
24164         this.picker().show();
24165         this.picker().select('>.datepicker-months', true).first().show();
24166         this.update();
24167         this.place();
24168         
24169         this.fireEvent('show', this, this.date);
24170     },
24171     
24172     hide : function()
24173     {
24174         if(this.isInline) {
24175             return;
24176         }
24177         this.picker().hide();
24178         this.fireEvent('hide', this, this.date);
24179         
24180     },
24181     
24182     onMousedown: function(e)
24183     {
24184         e.stopPropagation();
24185         e.preventDefault();
24186     },
24187     
24188     keyup: function(e)
24189     {
24190         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24191         this.update();
24192     },
24193
24194     fireKey: function(e)
24195     {
24196         if (!this.picker().isVisible()){
24197             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24198                 this.show();
24199             }
24200             return;
24201         }
24202         
24203         var dir;
24204         
24205         switch(e.keyCode){
24206             case 27: // escape
24207                 this.hide();
24208                 e.preventDefault();
24209                 break;
24210             case 37: // left
24211             case 39: // right
24212                 dir = e.keyCode == 37 ? -1 : 1;
24213                 
24214                 this.vIndex = this.vIndex + dir;
24215                 
24216                 if(this.vIndex < 0){
24217                     this.vIndex = 0;
24218                 }
24219                 
24220                 if(this.vIndex > 11){
24221                     this.vIndex = 11;
24222                 }
24223                 
24224                 if(isNaN(this.vIndex)){
24225                     this.vIndex = 0;
24226                 }
24227                 
24228                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24229                 
24230                 break;
24231             case 38: // up
24232             case 40: // down
24233                 
24234                 dir = e.keyCode == 38 ? -1 : 1;
24235                 
24236                 this.vIndex = this.vIndex + dir * 4;
24237                 
24238                 if(this.vIndex < 0){
24239                     this.vIndex = 0;
24240                 }
24241                 
24242                 if(this.vIndex > 11){
24243                     this.vIndex = 11;
24244                 }
24245                 
24246                 if(isNaN(this.vIndex)){
24247                     this.vIndex = 0;
24248                 }
24249                 
24250                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24251                 break;
24252                 
24253             case 13: // enter
24254                 
24255                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24256                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24257                 }
24258                 
24259                 this.hide();
24260                 e.preventDefault();
24261                 break;
24262             case 9: // tab
24263                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24264                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24265                 }
24266                 this.hide();
24267                 break;
24268             case 16: // shift
24269             case 17: // ctrl
24270             case 18: // alt
24271                 break;
24272             default :
24273                 this.hide();
24274                 
24275         }
24276     },
24277     
24278     remove: function() 
24279     {
24280         this.picker().remove();
24281     }
24282    
24283 });
24284
24285 Roo.apply(Roo.bootstrap.MonthField,  {
24286     
24287     content : {
24288         tag: 'tbody',
24289         cn: [
24290         {
24291             tag: 'tr',
24292             cn: [
24293             {
24294                 tag: 'td',
24295                 colspan: '7'
24296             }
24297             ]
24298         }
24299         ]
24300     },
24301     
24302     dates:{
24303         en: {
24304             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24305             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24306         }
24307     }
24308 });
24309
24310 Roo.apply(Roo.bootstrap.MonthField,  {
24311   
24312     template : {
24313         tag: 'div',
24314         cls: 'datepicker dropdown-menu roo-dynamic',
24315         cn: [
24316             {
24317                 tag: 'div',
24318                 cls: 'datepicker-months',
24319                 cn: [
24320                 {
24321                     tag: 'table',
24322                     cls: 'table-condensed',
24323                     cn:[
24324                         Roo.bootstrap.DateField.content
24325                     ]
24326                 }
24327                 ]
24328             }
24329         ]
24330     }
24331 });
24332
24333  
24334
24335  
24336  /*
24337  * - LGPL
24338  *
24339  * CheckBox
24340  * 
24341  */
24342
24343 /**
24344  * @class Roo.bootstrap.CheckBox
24345  * @extends Roo.bootstrap.Input
24346  * Bootstrap CheckBox class
24347  * 
24348  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24349  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24350  * @cfg {String} boxLabel The text that appears beside the checkbox
24351  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24352  * @cfg {Boolean} checked initnal the element
24353  * @cfg {Boolean} inline inline the element (default false)
24354  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24355  * @cfg {String} tooltip label tooltip
24356  * 
24357  * @constructor
24358  * Create a new CheckBox
24359  * @param {Object} config The config object
24360  */
24361
24362 Roo.bootstrap.CheckBox = function(config){
24363     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24364    
24365     this.addEvents({
24366         /**
24367         * @event check
24368         * Fires when the element is checked or unchecked.
24369         * @param {Roo.bootstrap.CheckBox} this This input
24370         * @param {Boolean} checked The new checked value
24371         */
24372        check : true,
24373        /**
24374         * @event click
24375         * Fires when the element is click.
24376         * @param {Roo.bootstrap.CheckBox} this This input
24377         */
24378        click : true
24379     });
24380     
24381 };
24382
24383 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24384   
24385     inputType: 'checkbox',
24386     inputValue: 1,
24387     valueOff: 0,
24388     boxLabel: false,
24389     checked: false,
24390     weight : false,
24391     inline: false,
24392     tooltip : '',
24393     
24394     // checkbox success does not make any sense really.. 
24395     invalidClass : "",
24396     validClass : "",
24397     
24398     
24399     getAutoCreate : function()
24400     {
24401         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24402         
24403         var id = Roo.id();
24404         
24405         var cfg = {};
24406         
24407         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24408         
24409         if(this.inline){
24410             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24411         }
24412         
24413         var input =  {
24414             tag: 'input',
24415             id : id,
24416             type : this.inputType,
24417             value : this.inputValue,
24418             cls : 'roo-' + this.inputType, //'form-box',
24419             placeholder : this.placeholder || ''
24420             
24421         };
24422         
24423         if(this.inputType != 'radio'){
24424             var hidden =  {
24425                 tag: 'input',
24426                 type : 'hidden',
24427                 cls : 'roo-hidden-value',
24428                 value : this.checked ? this.inputValue : this.valueOff
24429             };
24430         }
24431         
24432             
24433         if (this.weight) { // Validity check?
24434             cfg.cls += " " + this.inputType + "-" + this.weight;
24435         }
24436         
24437         if (this.disabled) {
24438             input.disabled=true;
24439         }
24440         
24441         if(this.checked){
24442             input.checked = this.checked;
24443         }
24444         
24445         if (this.name) {
24446             
24447             input.name = this.name;
24448             
24449             if(this.inputType != 'radio'){
24450                 hidden.name = this.name;
24451                 input.name = '_hidden_' + this.name;
24452             }
24453         }
24454         
24455         if (this.size) {
24456             input.cls += ' input-' + this.size;
24457         }
24458         
24459         var settings=this;
24460         
24461         ['xs','sm','md','lg'].map(function(size){
24462             if (settings[size]) {
24463                 cfg.cls += ' col-' + size + '-' + settings[size];
24464             }
24465         });
24466         
24467         var inputblock = input;
24468          
24469         if (this.before || this.after) {
24470             
24471             inputblock = {
24472                 cls : 'input-group',
24473                 cn :  [] 
24474             };
24475             
24476             if (this.before) {
24477                 inputblock.cn.push({
24478                     tag :'span',
24479                     cls : 'input-group-addon',
24480                     html : this.before
24481                 });
24482             }
24483             
24484             inputblock.cn.push(input);
24485             
24486             if(this.inputType != 'radio'){
24487                 inputblock.cn.push(hidden);
24488             }
24489             
24490             if (this.after) {
24491                 inputblock.cn.push({
24492                     tag :'span',
24493                     cls : 'input-group-addon',
24494                     html : this.after
24495                 });
24496             }
24497             
24498         }
24499         var boxLabelCfg = false;
24500         
24501         if(this.boxLabel){
24502            
24503             boxLabelCfg = {
24504                 tag: 'label',
24505                 //'for': id, // box label is handled by onclick - so no for...
24506                 cls: 'box-label',
24507                 html: this.boxLabel
24508             };
24509             if(this.tooltip){
24510                 boxLabelCfg.tooltip = this.tooltip;
24511             }
24512              
24513         }
24514         
24515         
24516         if (align ==='left' && this.fieldLabel.length) {
24517 //                Roo.log("left and has label");
24518             cfg.cn = [
24519                 {
24520                     tag: 'label',
24521                     'for' :  id,
24522                     cls : 'control-label',
24523                     html : this.fieldLabel
24524                 },
24525                 {
24526                     cls : "", 
24527                     cn: [
24528                         inputblock
24529                     ]
24530                 }
24531             ];
24532             
24533             if (boxLabelCfg) {
24534                 cfg.cn[1].cn.push(boxLabelCfg);
24535             }
24536             
24537             if(this.labelWidth > 12){
24538                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24539             }
24540             
24541             if(this.labelWidth < 13 && this.labelmd == 0){
24542                 this.labelmd = this.labelWidth;
24543             }
24544             
24545             if(this.labellg > 0){
24546                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24547                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24548             }
24549             
24550             if(this.labelmd > 0){
24551                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24552                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24553             }
24554             
24555             if(this.labelsm > 0){
24556                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24557                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24558             }
24559             
24560             if(this.labelxs > 0){
24561                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24562                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24563             }
24564             
24565         } else if ( this.fieldLabel.length) {
24566 //                Roo.log(" label");
24567                 cfg.cn = [
24568                    
24569                     {
24570                         tag: this.boxLabel ? 'span' : 'label',
24571                         'for': id,
24572                         cls: 'control-label box-input-label',
24573                         //cls : 'input-group-addon',
24574                         html : this.fieldLabel
24575                     },
24576                     
24577                     inputblock
24578                     
24579                 ];
24580                 if (boxLabelCfg) {
24581                     cfg.cn.push(boxLabelCfg);
24582                 }
24583
24584         } else {
24585             
24586 //                Roo.log(" no label && no align");
24587                 cfg.cn = [  inputblock ] ;
24588                 if (boxLabelCfg) {
24589                     cfg.cn.push(boxLabelCfg);
24590                 }
24591
24592                 
24593         }
24594         
24595        
24596         
24597         if(this.inputType != 'radio'){
24598             cfg.cn.push(hidden);
24599         }
24600         
24601         return cfg;
24602         
24603     },
24604     
24605     /**
24606      * return the real input element.
24607      */
24608     inputEl: function ()
24609     {
24610         return this.el.select('input.roo-' + this.inputType,true).first();
24611     },
24612     hiddenEl: function ()
24613     {
24614         return this.el.select('input.roo-hidden-value',true).first();
24615     },
24616     
24617     labelEl: function()
24618     {
24619         return this.el.select('label.control-label',true).first();
24620     },
24621     /* depricated... */
24622     
24623     label: function()
24624     {
24625         return this.labelEl();
24626     },
24627     
24628     boxLabelEl: function()
24629     {
24630         return this.el.select('label.box-label',true).first();
24631     },
24632     
24633     initEvents : function()
24634     {
24635 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24636         
24637         this.inputEl().on('click', this.onClick,  this);
24638         
24639         if (this.boxLabel) { 
24640             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24641         }
24642         
24643         this.startValue = this.getValue();
24644         
24645         if(this.groupId){
24646             Roo.bootstrap.CheckBox.register(this);
24647         }
24648     },
24649     
24650     onClick : function(e)
24651     {   
24652         if(this.fireEvent('click', this, e) !== false){
24653             this.setChecked(!this.checked);
24654         }
24655         
24656     },
24657     
24658     setChecked : function(state,suppressEvent)
24659     {
24660         this.startValue = this.getValue();
24661
24662         if(this.inputType == 'radio'){
24663             
24664             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24665                 e.dom.checked = false;
24666             });
24667             
24668             this.inputEl().dom.checked = true;
24669             
24670             this.inputEl().dom.value = this.inputValue;
24671             
24672             if(suppressEvent !== true){
24673                 this.fireEvent('check', this, true);
24674             }
24675             
24676             this.validate();
24677             
24678             return;
24679         }
24680         
24681         this.checked = state;
24682         
24683         this.inputEl().dom.checked = state;
24684         
24685         
24686         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24687         
24688         if(suppressEvent !== true){
24689             this.fireEvent('check', this, state);
24690         }
24691         
24692         this.validate();
24693     },
24694     
24695     getValue : function()
24696     {
24697         if(this.inputType == 'radio'){
24698             return this.getGroupValue();
24699         }
24700         
24701         return this.hiddenEl().dom.value;
24702         
24703     },
24704     
24705     getGroupValue : function()
24706     {
24707         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24708             return '';
24709         }
24710         
24711         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24712     },
24713     
24714     setValue : function(v,suppressEvent)
24715     {
24716         if(this.inputType == 'radio'){
24717             this.setGroupValue(v, suppressEvent);
24718             return;
24719         }
24720         
24721         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24722         
24723         this.validate();
24724     },
24725     
24726     setGroupValue : function(v, suppressEvent)
24727     {
24728         this.startValue = this.getValue();
24729         
24730         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24731             e.dom.checked = false;
24732             
24733             if(e.dom.value == v){
24734                 e.dom.checked = true;
24735             }
24736         });
24737         
24738         if(suppressEvent !== true){
24739             this.fireEvent('check', this, true);
24740         }
24741
24742         this.validate();
24743         
24744         return;
24745     },
24746     
24747     validate : function()
24748     {
24749         if(this.getVisibilityEl().hasClass('hidden')){
24750             return true;
24751         }
24752         
24753         if(
24754                 this.disabled || 
24755                 (this.inputType == 'radio' && this.validateRadio()) ||
24756                 (this.inputType == 'checkbox' && this.validateCheckbox())
24757         ){
24758             this.markValid();
24759             return true;
24760         }
24761         
24762         this.markInvalid();
24763         return false;
24764     },
24765     
24766     validateRadio : function()
24767     {
24768         if(this.getVisibilityEl().hasClass('hidden')){
24769             return true;
24770         }
24771         
24772         if(this.allowBlank){
24773             return true;
24774         }
24775         
24776         var valid = false;
24777         
24778         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24779             if(!e.dom.checked){
24780                 return;
24781             }
24782             
24783             valid = true;
24784             
24785             return false;
24786         });
24787         
24788         return valid;
24789     },
24790     
24791     validateCheckbox : function()
24792     {
24793         if(!this.groupId){
24794             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24795             //return (this.getValue() == this.inputValue) ? true : false;
24796         }
24797         
24798         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24799         
24800         if(!group){
24801             return false;
24802         }
24803         
24804         var r = false;
24805         
24806         for(var i in group){
24807             if(group[i].el.isVisible(true)){
24808                 r = false;
24809                 break;
24810             }
24811             
24812             r = true;
24813         }
24814         
24815         for(var i in group){
24816             if(r){
24817                 break;
24818             }
24819             
24820             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24821         }
24822         
24823         return r;
24824     },
24825     
24826     /**
24827      * Mark this field as valid
24828      */
24829     markValid : function()
24830     {
24831         var _this = this;
24832         
24833         this.fireEvent('valid', this);
24834         
24835         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24836         
24837         if(this.groupId){
24838             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24839         }
24840         
24841         if(label){
24842             label.markValid();
24843         }
24844
24845         if(this.inputType == 'radio'){
24846             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24847                 var fg = e.findParent('.form-group', false, true);
24848                 if (Roo.bootstrap.version == 3) {
24849                     fg.removeClass([_this.invalidClass, _this.validClass]);
24850                     fg.addClass(_this.validClass);
24851                 } else {
24852                     fg.removeClass(['is-valid', 'is-invalid']);
24853                     fg.addClass('is-valid');
24854                 }
24855             });
24856             
24857             return;
24858         }
24859
24860         if(!this.groupId){
24861             var fg = this.el.findParent('.form-group', false, true);
24862             if (Roo.bootstrap.version == 3) {
24863                 fg.removeClass([this.invalidClass, this.validClass]);
24864                 fg.addClass(this.validClass);
24865             } else {
24866                 fg.removeClass(['is-valid', 'is-invalid']);
24867                 fg.addClass('is-valid');
24868             }
24869             return;
24870         }
24871         
24872         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24873         
24874         if(!group){
24875             return;
24876         }
24877         
24878         for(var i in group){
24879             var fg = group[i].el.findParent('.form-group', false, true);
24880             if (Roo.bootstrap.version == 3) {
24881                 fg.removeClass([this.invalidClass, this.validClass]);
24882                 fg.addClass(this.validClass);
24883             } else {
24884                 fg.removeClass(['is-valid', 'is-invalid']);
24885                 fg.addClass('is-valid');
24886             }
24887         }
24888     },
24889     
24890      /**
24891      * Mark this field as invalid
24892      * @param {String} msg The validation message
24893      */
24894     markInvalid : function(msg)
24895     {
24896         if(this.allowBlank){
24897             return;
24898         }
24899         
24900         var _this = this;
24901         
24902         this.fireEvent('invalid', this, msg);
24903         
24904         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24905         
24906         if(this.groupId){
24907             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24908         }
24909         
24910         if(label){
24911             label.markInvalid();
24912         }
24913             
24914         if(this.inputType == 'radio'){
24915             
24916             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24917                 var fg = e.findParent('.form-group', false, true);
24918                 if (Roo.bootstrap.version == 3) {
24919                     fg.removeClass([_this.invalidClass, _this.validClass]);
24920                     fg.addClass(_this.invalidClass);
24921                 } else {
24922                     fg.removeClass(['is-invalid', 'is-valid']);
24923                     fg.addClass('is-invalid');
24924                 }
24925             });
24926             
24927             return;
24928         }
24929         
24930         if(!this.groupId){
24931             var fg = this.el.findParent('.form-group', false, true);
24932             if (Roo.bootstrap.version == 3) {
24933                 fg.removeClass([_this.invalidClass, _this.validClass]);
24934                 fg.addClass(_this.invalidClass);
24935             } else {
24936                 fg.removeClass(['is-invalid', 'is-valid']);
24937                 fg.addClass('is-invalid');
24938             }
24939             return;
24940         }
24941         
24942         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24943         
24944         if(!group){
24945             return;
24946         }
24947         
24948         for(var i in group){
24949             var fg = group[i].el.findParent('.form-group', false, true);
24950             if (Roo.bootstrap.version == 3) {
24951                 fg.removeClass([_this.invalidClass, _this.validClass]);
24952                 fg.addClass(_this.invalidClass);
24953             } else {
24954                 fg.removeClass(['is-invalid', 'is-valid']);
24955                 fg.addClass('is-invalid');
24956             }
24957         }
24958         
24959     },
24960     
24961     clearInvalid : function()
24962     {
24963         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24964         
24965         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24966         
24967         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24968         
24969         if (label && label.iconEl) {
24970             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24971             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24972         }
24973     },
24974     
24975     disable : function()
24976     {
24977         if(this.inputType != 'radio'){
24978             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24979             return;
24980         }
24981         
24982         var _this = this;
24983         
24984         if(this.rendered){
24985             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24986                 _this.getActionEl().addClass(this.disabledClass);
24987                 e.dom.disabled = true;
24988             });
24989         }
24990         
24991         this.disabled = true;
24992         this.fireEvent("disable", this);
24993         return this;
24994     },
24995
24996     enable : function()
24997     {
24998         if(this.inputType != 'radio'){
24999             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25000             return;
25001         }
25002         
25003         var _this = this;
25004         
25005         if(this.rendered){
25006             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25007                 _this.getActionEl().removeClass(this.disabledClass);
25008                 e.dom.disabled = false;
25009             });
25010         }
25011         
25012         this.disabled = false;
25013         this.fireEvent("enable", this);
25014         return this;
25015     },
25016     
25017     setBoxLabel : function(v)
25018     {
25019         this.boxLabel = v;
25020         
25021         if(this.rendered){
25022             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25023         }
25024     }
25025
25026 });
25027
25028 Roo.apply(Roo.bootstrap.CheckBox, {
25029     
25030     groups: {},
25031     
25032      /**
25033     * register a CheckBox Group
25034     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25035     */
25036     register : function(checkbox)
25037     {
25038         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25039             this.groups[checkbox.groupId] = {};
25040         }
25041         
25042         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25043             return;
25044         }
25045         
25046         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25047         
25048     },
25049     /**
25050     * fetch a CheckBox Group based on the group ID
25051     * @param {string} the group ID
25052     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25053     */
25054     get: function(groupId) {
25055         if (typeof(this.groups[groupId]) == 'undefined') {
25056             return false;
25057         }
25058         
25059         return this.groups[groupId] ;
25060     }
25061     
25062     
25063 });
25064 /*
25065  * - LGPL
25066  *
25067  * RadioItem
25068  * 
25069  */
25070
25071 /**
25072  * @class Roo.bootstrap.Radio
25073  * @extends Roo.bootstrap.Component
25074  * Bootstrap Radio class
25075  * @cfg {String} boxLabel - the label associated
25076  * @cfg {String} value - the value of radio
25077  * 
25078  * @constructor
25079  * Create a new Radio
25080  * @param {Object} config The config object
25081  */
25082 Roo.bootstrap.Radio = function(config){
25083     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25084     
25085 };
25086
25087 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25088     
25089     boxLabel : '',
25090     
25091     value : '',
25092     
25093     getAutoCreate : function()
25094     {
25095         var cfg = {
25096             tag : 'div',
25097             cls : 'form-group radio',
25098             cn : [
25099                 {
25100                     tag : 'label',
25101                     cls : 'box-label',
25102                     html : this.boxLabel
25103                 }
25104             ]
25105         };
25106         
25107         return cfg;
25108     },
25109     
25110     initEvents : function() 
25111     {
25112         this.parent().register(this);
25113         
25114         this.el.on('click', this.onClick, this);
25115         
25116     },
25117     
25118     onClick : function(e)
25119     {
25120         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25121             this.setChecked(true);
25122         }
25123     },
25124     
25125     setChecked : function(state, suppressEvent)
25126     {
25127         this.parent().setValue(this.value, suppressEvent);
25128         
25129     },
25130     
25131     setBoxLabel : function(v)
25132     {
25133         this.boxLabel = v;
25134         
25135         if(this.rendered){
25136             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25137         }
25138     }
25139     
25140 });
25141  
25142
25143  /*
25144  * - LGPL
25145  *
25146  * Input
25147  * 
25148  */
25149
25150 /**
25151  * @class Roo.bootstrap.SecurePass
25152  * @extends Roo.bootstrap.Input
25153  * Bootstrap SecurePass class
25154  *
25155  * 
25156  * @constructor
25157  * Create a new SecurePass
25158  * @param {Object} config The config object
25159  */
25160  
25161 Roo.bootstrap.SecurePass = function (config) {
25162     // these go here, so the translation tool can replace them..
25163     this.errors = {
25164         PwdEmpty: "Please type a password, and then retype it to confirm.",
25165         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25166         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25167         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25168         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25169         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25170         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25171         TooWeak: "Your password is Too Weak."
25172     },
25173     this.meterLabel = "Password strength:";
25174     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25175     this.meterClass = [
25176         "roo-password-meter-tooweak", 
25177         "roo-password-meter-weak", 
25178         "roo-password-meter-medium", 
25179         "roo-password-meter-strong", 
25180         "roo-password-meter-grey"
25181     ];
25182     
25183     this.errors = {};
25184     
25185     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25186 }
25187
25188 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25189     /**
25190      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25191      * {
25192      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25193      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25194      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25195      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25196      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25197      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25198      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25199      * })
25200      */
25201     // private
25202     
25203     meterWidth: 300,
25204     errorMsg :'',    
25205     errors: false,
25206     imageRoot: '/',
25207     /**
25208      * @cfg {String/Object} Label for the strength meter (defaults to
25209      * 'Password strength:')
25210      */
25211     // private
25212     meterLabel: '',
25213     /**
25214      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25215      * ['Weak', 'Medium', 'Strong'])
25216      */
25217     // private    
25218     pwdStrengths: false,    
25219     // private
25220     strength: 0,
25221     // private
25222     _lastPwd: null,
25223     // private
25224     kCapitalLetter: 0,
25225     kSmallLetter: 1,
25226     kDigit: 2,
25227     kPunctuation: 3,
25228     
25229     insecure: false,
25230     // private
25231     initEvents: function ()
25232     {
25233         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25234
25235         if (this.el.is('input[type=password]') && Roo.isSafari) {
25236             this.el.on('keydown', this.SafariOnKeyDown, this);
25237         }
25238
25239         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25240     },
25241     // private
25242     onRender: function (ct, position)
25243     {
25244         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25245         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25246         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25247
25248         this.trigger.createChild({
25249                    cn: [
25250                     {
25251                     //id: 'PwdMeter',
25252                     tag: 'div',
25253                     cls: 'roo-password-meter-grey col-xs-12',
25254                     style: {
25255                         //width: 0,
25256                         //width: this.meterWidth + 'px'                                                
25257                         }
25258                     },
25259                     {                            
25260                          cls: 'roo-password-meter-text'                          
25261                     }
25262                 ]            
25263         });
25264
25265          
25266         if (this.hideTrigger) {
25267             this.trigger.setDisplayed(false);
25268         }
25269         this.setSize(this.width || '', this.height || '');
25270     },
25271     // private
25272     onDestroy: function ()
25273     {
25274         if (this.trigger) {
25275             this.trigger.removeAllListeners();
25276             this.trigger.remove();
25277         }
25278         if (this.wrap) {
25279             this.wrap.remove();
25280         }
25281         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25282     },
25283     // private
25284     checkStrength: function ()
25285     {
25286         var pwd = this.inputEl().getValue();
25287         if (pwd == this._lastPwd) {
25288             return;
25289         }
25290
25291         var strength;
25292         if (this.ClientSideStrongPassword(pwd)) {
25293             strength = 3;
25294         } else if (this.ClientSideMediumPassword(pwd)) {
25295             strength = 2;
25296         } else if (this.ClientSideWeakPassword(pwd)) {
25297             strength = 1;
25298         } else {
25299             strength = 0;
25300         }
25301         
25302         Roo.log('strength1: ' + strength);
25303         
25304         //var pm = this.trigger.child('div/div/div').dom;
25305         var pm = this.trigger.child('div/div');
25306         pm.removeClass(this.meterClass);
25307         pm.addClass(this.meterClass[strength]);
25308                 
25309         
25310         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25311                 
25312         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25313         
25314         this._lastPwd = pwd;
25315     },
25316     reset: function ()
25317     {
25318         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25319         
25320         this._lastPwd = '';
25321         
25322         var pm = this.trigger.child('div/div');
25323         pm.removeClass(this.meterClass);
25324         pm.addClass('roo-password-meter-grey');        
25325         
25326         
25327         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25328         
25329         pt.innerHTML = '';
25330         this.inputEl().dom.type='password';
25331     },
25332     // private
25333     validateValue: function (value)
25334     {
25335         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25336             return false;
25337         }
25338         if (value.length == 0) {
25339             if (this.allowBlank) {
25340                 this.clearInvalid();
25341                 return true;
25342             }
25343
25344             this.markInvalid(this.errors.PwdEmpty);
25345             this.errorMsg = this.errors.PwdEmpty;
25346             return false;
25347         }
25348         
25349         if(this.insecure){
25350             return true;
25351         }
25352         
25353         if (!value.match(/[\x21-\x7e]+/)) {
25354             this.markInvalid(this.errors.PwdBadChar);
25355             this.errorMsg = this.errors.PwdBadChar;
25356             return false;
25357         }
25358         if (value.length < 6) {
25359             this.markInvalid(this.errors.PwdShort);
25360             this.errorMsg = this.errors.PwdShort;
25361             return false;
25362         }
25363         if (value.length > 16) {
25364             this.markInvalid(this.errors.PwdLong);
25365             this.errorMsg = this.errors.PwdLong;
25366             return false;
25367         }
25368         var strength;
25369         if (this.ClientSideStrongPassword(value)) {
25370             strength = 3;
25371         } else if (this.ClientSideMediumPassword(value)) {
25372             strength = 2;
25373         } else if (this.ClientSideWeakPassword(value)) {
25374             strength = 1;
25375         } else {
25376             strength = 0;
25377         }
25378
25379         
25380         if (strength < 2) {
25381             //this.markInvalid(this.errors.TooWeak);
25382             this.errorMsg = this.errors.TooWeak;
25383             //return false;
25384         }
25385         
25386         
25387         console.log('strength2: ' + strength);
25388         
25389         //var pm = this.trigger.child('div/div/div').dom;
25390         
25391         var pm = this.trigger.child('div/div');
25392         pm.removeClass(this.meterClass);
25393         pm.addClass(this.meterClass[strength]);
25394                 
25395         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25396                 
25397         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25398         
25399         this.errorMsg = ''; 
25400         return true;
25401     },
25402     // private
25403     CharacterSetChecks: function (type)
25404     {
25405         this.type = type;
25406         this.fResult = false;
25407     },
25408     // private
25409     isctype: function (character, type)
25410     {
25411         switch (type) {  
25412             case this.kCapitalLetter:
25413                 if (character >= 'A' && character <= 'Z') {
25414                     return true;
25415                 }
25416                 break;
25417             
25418             case this.kSmallLetter:
25419                 if (character >= 'a' && character <= 'z') {
25420                     return true;
25421                 }
25422                 break;
25423             
25424             case this.kDigit:
25425                 if (character >= '0' && character <= '9') {
25426                     return true;
25427                 }
25428                 break;
25429             
25430             case this.kPunctuation:
25431                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25432                     return true;
25433                 }
25434                 break;
25435             
25436             default:
25437                 return false;
25438         }
25439
25440     },
25441     // private
25442     IsLongEnough: function (pwd, size)
25443     {
25444         return !(pwd == null || isNaN(size) || pwd.length < size);
25445     },
25446     // private
25447     SpansEnoughCharacterSets: function (word, nb)
25448     {
25449         if (!this.IsLongEnough(word, nb))
25450         {
25451             return false;
25452         }
25453
25454         var characterSetChecks = new Array(
25455             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25456             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25457         );
25458         
25459         for (var index = 0; index < word.length; ++index) {
25460             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25461                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25462                     characterSetChecks[nCharSet].fResult = true;
25463                     break;
25464                 }
25465             }
25466         }
25467
25468         var nCharSets = 0;
25469         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25470             if (characterSetChecks[nCharSet].fResult) {
25471                 ++nCharSets;
25472             }
25473         }
25474
25475         if (nCharSets < nb) {
25476             return false;
25477         }
25478         return true;
25479     },
25480     // private
25481     ClientSideStrongPassword: function (pwd)
25482     {
25483         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25484     },
25485     // private
25486     ClientSideMediumPassword: function (pwd)
25487     {
25488         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25489     },
25490     // private
25491     ClientSideWeakPassword: function (pwd)
25492     {
25493         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25494     }
25495           
25496 })//<script type="text/javascript">
25497
25498 /*
25499  * Based  Ext JS Library 1.1.1
25500  * Copyright(c) 2006-2007, Ext JS, LLC.
25501  * LGPL
25502  *
25503  */
25504  
25505 /**
25506  * @class Roo.HtmlEditorCore
25507  * @extends Roo.Component
25508  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25509  *
25510  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25511  */
25512
25513 Roo.HtmlEditorCore = function(config){
25514     
25515     
25516     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25517     
25518     
25519     this.addEvents({
25520         /**
25521          * @event initialize
25522          * Fires when the editor is fully initialized (including the iframe)
25523          * @param {Roo.HtmlEditorCore} this
25524          */
25525         initialize: true,
25526         /**
25527          * @event activate
25528          * Fires when the editor is first receives the focus. Any insertion must wait
25529          * until after this event.
25530          * @param {Roo.HtmlEditorCore} this
25531          */
25532         activate: true,
25533          /**
25534          * @event beforesync
25535          * Fires before the textarea is updated with content from the editor iframe. Return false
25536          * to cancel the sync.
25537          * @param {Roo.HtmlEditorCore} this
25538          * @param {String} html
25539          */
25540         beforesync: true,
25541          /**
25542          * @event beforepush
25543          * Fires before the iframe editor is updated with content from the textarea. Return false
25544          * to cancel the push.
25545          * @param {Roo.HtmlEditorCore} this
25546          * @param {String} html
25547          */
25548         beforepush: true,
25549          /**
25550          * @event sync
25551          * Fires when the textarea is updated with content from the editor iframe.
25552          * @param {Roo.HtmlEditorCore} this
25553          * @param {String} html
25554          */
25555         sync: true,
25556          /**
25557          * @event push
25558          * Fires when the iframe editor is updated with content from the textarea.
25559          * @param {Roo.HtmlEditorCore} this
25560          * @param {String} html
25561          */
25562         push: true,
25563         
25564         /**
25565          * @event editorevent
25566          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25567          * @param {Roo.HtmlEditorCore} this
25568          */
25569         editorevent: true
25570         
25571     });
25572     
25573     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25574     
25575     // defaults : white / black...
25576     this.applyBlacklists();
25577     
25578     
25579     
25580 };
25581
25582
25583 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25584
25585
25586      /**
25587      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25588      */
25589     
25590     owner : false,
25591     
25592      /**
25593      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25594      *                        Roo.resizable.
25595      */
25596     resizable : false,
25597      /**
25598      * @cfg {Number} height (in pixels)
25599      */   
25600     height: 300,
25601    /**
25602      * @cfg {Number} width (in pixels)
25603      */   
25604     width: 500,
25605     
25606     /**
25607      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25608      * 
25609      */
25610     stylesheets: false,
25611     
25612     // id of frame..
25613     frameId: false,
25614     
25615     // private properties
25616     validationEvent : false,
25617     deferHeight: true,
25618     initialized : false,
25619     activated : false,
25620     sourceEditMode : false,
25621     onFocus : Roo.emptyFn,
25622     iframePad:3,
25623     hideMode:'offsets',
25624     
25625     clearUp: true,
25626     
25627     // blacklist + whitelisted elements..
25628     black: false,
25629     white: false,
25630      
25631     bodyCls : '',
25632
25633     /**
25634      * Protected method that will not generally be called directly. It
25635      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25636      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25637      */
25638     getDocMarkup : function(){
25639         // body styles..
25640         var st = '';
25641         
25642         // inherit styels from page...?? 
25643         if (this.stylesheets === false) {
25644             
25645             Roo.get(document.head).select('style').each(function(node) {
25646                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25647             });
25648             
25649             Roo.get(document.head).select('link').each(function(node) { 
25650                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25651             });
25652             
25653         } else if (!this.stylesheets.length) {
25654                 // simple..
25655                 st = '<style type="text/css">' +
25656                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25657                    '</style>';
25658         } else {
25659             for (var i in this.stylesheets) { 
25660                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25661             }
25662             
25663         }
25664         
25665         st +=  '<style type="text/css">' +
25666             'IMG { cursor: pointer } ' +
25667         '</style>';
25668
25669         var cls = 'roo-htmleditor-body';
25670         
25671         if(this.bodyCls.length){
25672             cls += ' ' + this.bodyCls;
25673         }
25674         
25675         return '<html><head>' + st  +
25676             //<style type="text/css">' +
25677             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25678             //'</style>' +
25679             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25680     },
25681
25682     // private
25683     onRender : function(ct, position)
25684     {
25685         var _t = this;
25686         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25687         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25688         
25689         
25690         this.el.dom.style.border = '0 none';
25691         this.el.dom.setAttribute('tabIndex', -1);
25692         this.el.addClass('x-hidden hide');
25693         
25694         
25695         
25696         if(Roo.isIE){ // fix IE 1px bogus margin
25697             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25698         }
25699        
25700         
25701         this.frameId = Roo.id();
25702         
25703          
25704         
25705         var iframe = this.owner.wrap.createChild({
25706             tag: 'iframe',
25707             cls: 'form-control', // bootstrap..
25708             id: this.frameId,
25709             name: this.frameId,
25710             frameBorder : 'no',
25711             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25712         }, this.el
25713         );
25714         
25715         
25716         this.iframe = iframe.dom;
25717
25718          this.assignDocWin();
25719         
25720         this.doc.designMode = 'on';
25721        
25722         this.doc.open();
25723         this.doc.write(this.getDocMarkup());
25724         this.doc.close();
25725
25726         
25727         var task = { // must defer to wait for browser to be ready
25728             run : function(){
25729                 //console.log("run task?" + this.doc.readyState);
25730                 this.assignDocWin();
25731                 if(this.doc.body || this.doc.readyState == 'complete'){
25732                     try {
25733                         this.doc.designMode="on";
25734                     } catch (e) {
25735                         return;
25736                     }
25737                     Roo.TaskMgr.stop(task);
25738                     this.initEditor.defer(10, this);
25739                 }
25740             },
25741             interval : 10,
25742             duration: 10000,
25743             scope: this
25744         };
25745         Roo.TaskMgr.start(task);
25746
25747     },
25748
25749     // private
25750     onResize : function(w, h)
25751     {
25752          Roo.log('resize: ' +w + ',' + h );
25753         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25754         if(!this.iframe){
25755             return;
25756         }
25757         if(typeof w == 'number'){
25758             
25759             this.iframe.style.width = w + 'px';
25760         }
25761         if(typeof h == 'number'){
25762             
25763             this.iframe.style.height = h + 'px';
25764             if(this.doc){
25765                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25766             }
25767         }
25768         
25769     },
25770
25771     /**
25772      * Toggles the editor between standard and source edit mode.
25773      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25774      */
25775     toggleSourceEdit : function(sourceEditMode){
25776         
25777         this.sourceEditMode = sourceEditMode === true;
25778         
25779         if(this.sourceEditMode){
25780  
25781             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25782             
25783         }else{
25784             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25785             //this.iframe.className = '';
25786             this.deferFocus();
25787         }
25788         //this.setSize(this.owner.wrap.getSize());
25789         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25790     },
25791
25792     
25793   
25794
25795     /**
25796      * Protected method that will not generally be called directly. If you need/want
25797      * custom HTML cleanup, this is the method you should override.
25798      * @param {String} html The HTML to be cleaned
25799      * return {String} The cleaned HTML
25800      */
25801     cleanHtml : function(html){
25802         html = String(html);
25803         if(html.length > 5){
25804             if(Roo.isSafari){ // strip safari nonsense
25805                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25806             }
25807         }
25808         if(html == '&nbsp;'){
25809             html = '';
25810         }
25811         return html;
25812     },
25813
25814     /**
25815      * HTML Editor -> Textarea
25816      * Protected method that will not generally be called directly. Syncs the contents
25817      * of the editor iframe with the textarea.
25818      */
25819     syncValue : function(){
25820         if(this.initialized){
25821             var bd = (this.doc.body || this.doc.documentElement);
25822             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25823             var html = bd.innerHTML;
25824             if(Roo.isSafari){
25825                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25826                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25827                 if(m && m[1]){
25828                     html = '<div style="'+m[0]+'">' + html + '</div>';
25829                 }
25830             }
25831             html = this.cleanHtml(html);
25832             // fix up the special chars.. normaly like back quotes in word...
25833             // however we do not want to do this with chinese..
25834             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25835                 
25836                 var cc = match.charCodeAt();
25837
25838                 // Get the character value, handling surrogate pairs
25839                 if (match.length == 2) {
25840                     // It's a surrogate pair, calculate the Unicode code point
25841                     var high = match.charCodeAt(0) - 0xD800;
25842                     var low  = match.charCodeAt(1) - 0xDC00;
25843                     cc = (high * 0x400) + low + 0x10000;
25844                 }  else if (
25845                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25846                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25847                     (cc >= 0xf900 && cc < 0xfb00 )
25848                 ) {
25849                         return match;
25850                 }  
25851          
25852                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25853                 return "&#" + cc + ";";
25854                 
25855                 
25856             });
25857             
25858             
25859              
25860             if(this.owner.fireEvent('beforesync', this, html) !== false){
25861                 this.el.dom.value = html;
25862                 this.owner.fireEvent('sync', this, html);
25863             }
25864         }
25865     },
25866
25867     /**
25868      * Protected method that will not generally be called directly. Pushes the value of the textarea
25869      * into the iframe editor.
25870      */
25871     pushValue : function(){
25872         if(this.initialized){
25873             var v = this.el.dom.value.trim();
25874             
25875 //            if(v.length < 1){
25876 //                v = '&#160;';
25877 //            }
25878             
25879             if(this.owner.fireEvent('beforepush', this, v) !== false){
25880                 var d = (this.doc.body || this.doc.documentElement);
25881                 d.innerHTML = v;
25882                 this.cleanUpPaste();
25883                 this.el.dom.value = d.innerHTML;
25884                 this.owner.fireEvent('push', this, v);
25885             }
25886         }
25887     },
25888
25889     // private
25890     deferFocus : function(){
25891         this.focus.defer(10, this);
25892     },
25893
25894     // doc'ed in Field
25895     focus : function(){
25896         if(this.win && !this.sourceEditMode){
25897             this.win.focus();
25898         }else{
25899             this.el.focus();
25900         }
25901     },
25902     
25903     assignDocWin: function()
25904     {
25905         var iframe = this.iframe;
25906         
25907          if(Roo.isIE){
25908             this.doc = iframe.contentWindow.document;
25909             this.win = iframe.contentWindow;
25910         } else {
25911 //            if (!Roo.get(this.frameId)) {
25912 //                return;
25913 //            }
25914 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25915 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25916             
25917             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25918                 return;
25919             }
25920             
25921             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25922             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25923         }
25924     },
25925     
25926     // private
25927     initEditor : function(){
25928         //console.log("INIT EDITOR");
25929         this.assignDocWin();
25930         
25931         
25932         
25933         this.doc.designMode="on";
25934         this.doc.open();
25935         this.doc.write(this.getDocMarkup());
25936         this.doc.close();
25937         
25938         var dbody = (this.doc.body || this.doc.documentElement);
25939         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25940         // this copies styles from the containing element into thsi one..
25941         // not sure why we need all of this..
25942         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25943         
25944         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25945         //ss['background-attachment'] = 'fixed'; // w3c
25946         dbody.bgProperties = 'fixed'; // ie
25947         //Roo.DomHelper.applyStyles(dbody, ss);
25948         Roo.EventManager.on(this.doc, {
25949             //'mousedown': this.onEditorEvent,
25950             'mouseup': this.onEditorEvent,
25951             'dblclick': this.onEditorEvent,
25952             'click': this.onEditorEvent,
25953             'keyup': this.onEditorEvent,
25954             buffer:100,
25955             scope: this
25956         });
25957         if(Roo.isGecko){
25958             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25959         }
25960         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25961             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25962         }
25963         this.initialized = true;
25964
25965         this.owner.fireEvent('initialize', this);
25966         this.pushValue();
25967     },
25968
25969     // private
25970     onDestroy : function(){
25971         
25972         
25973         
25974         if(this.rendered){
25975             
25976             //for (var i =0; i < this.toolbars.length;i++) {
25977             //    // fixme - ask toolbars for heights?
25978             //    this.toolbars[i].onDestroy();
25979            // }
25980             
25981             //this.wrap.dom.innerHTML = '';
25982             //this.wrap.remove();
25983         }
25984     },
25985
25986     // private
25987     onFirstFocus : function(){
25988         
25989         this.assignDocWin();
25990         
25991         
25992         this.activated = true;
25993          
25994     
25995         if(Roo.isGecko){ // prevent silly gecko errors
25996             this.win.focus();
25997             var s = this.win.getSelection();
25998             if(!s.focusNode || s.focusNode.nodeType != 3){
25999                 var r = s.getRangeAt(0);
26000                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26001                 r.collapse(true);
26002                 this.deferFocus();
26003             }
26004             try{
26005                 this.execCmd('useCSS', true);
26006                 this.execCmd('styleWithCSS', false);
26007             }catch(e){}
26008         }
26009         this.owner.fireEvent('activate', this);
26010     },
26011
26012     // private
26013     adjustFont: function(btn){
26014         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26015         //if(Roo.isSafari){ // safari
26016         //    adjust *= 2;
26017        // }
26018         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26019         if(Roo.isSafari){ // safari
26020             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26021             v =  (v < 10) ? 10 : v;
26022             v =  (v > 48) ? 48 : v;
26023             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26024             
26025         }
26026         
26027         
26028         v = Math.max(1, v+adjust);
26029         
26030         this.execCmd('FontSize', v  );
26031     },
26032
26033     onEditorEvent : function(e)
26034     {
26035         this.owner.fireEvent('editorevent', this, e);
26036       //  this.updateToolbar();
26037         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26038     },
26039
26040     insertTag : function(tg)
26041     {
26042         // could be a bit smarter... -> wrap the current selected tRoo..
26043         if (tg.toLowerCase() == 'span' ||
26044             tg.toLowerCase() == 'code' ||
26045             tg.toLowerCase() == 'sup' ||
26046             tg.toLowerCase() == 'sub' 
26047             ) {
26048             
26049             range = this.createRange(this.getSelection());
26050             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26051             wrappingNode.appendChild(range.extractContents());
26052             range.insertNode(wrappingNode);
26053
26054             return;
26055             
26056             
26057             
26058         }
26059         this.execCmd("formatblock",   tg);
26060         
26061     },
26062     
26063     insertText : function(txt)
26064     {
26065         
26066         
26067         var range = this.createRange();
26068         range.deleteContents();
26069                //alert(Sender.getAttribute('label'));
26070                
26071         range.insertNode(this.doc.createTextNode(txt));
26072     } ,
26073     
26074      
26075
26076     /**
26077      * Executes a Midas editor command on the editor document and performs necessary focus and
26078      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26079      * @param {String} cmd The Midas command
26080      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26081      */
26082     relayCmd : function(cmd, value){
26083         this.win.focus();
26084         this.execCmd(cmd, value);
26085         this.owner.fireEvent('editorevent', this);
26086         //this.updateToolbar();
26087         this.owner.deferFocus();
26088     },
26089
26090     /**
26091      * Executes a Midas editor command directly on the editor document.
26092      * For visual commands, you should use {@link #relayCmd} instead.
26093      * <b>This should only be called after the editor is initialized.</b>
26094      * @param {String} cmd The Midas command
26095      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26096      */
26097     execCmd : function(cmd, value){
26098         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26099         this.syncValue();
26100     },
26101  
26102  
26103    
26104     /**
26105      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26106      * to insert tRoo.
26107      * @param {String} text | dom node.. 
26108      */
26109     insertAtCursor : function(text)
26110     {
26111         
26112         if(!this.activated){
26113             return;
26114         }
26115         /*
26116         if(Roo.isIE){
26117             this.win.focus();
26118             var r = this.doc.selection.createRange();
26119             if(r){
26120                 r.collapse(true);
26121                 r.pasteHTML(text);
26122                 this.syncValue();
26123                 this.deferFocus();
26124             
26125             }
26126             return;
26127         }
26128         */
26129         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26130             this.win.focus();
26131             
26132             
26133             // from jquery ui (MIT licenced)
26134             var range, node;
26135             var win = this.win;
26136             
26137             if (win.getSelection && win.getSelection().getRangeAt) {
26138                 range = win.getSelection().getRangeAt(0);
26139                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26140                 range.insertNode(node);
26141             } else if (win.document.selection && win.document.selection.createRange) {
26142                 // no firefox support
26143                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26144                 win.document.selection.createRange().pasteHTML(txt);
26145             } else {
26146                 // no firefox support
26147                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26148                 this.execCmd('InsertHTML', txt);
26149             } 
26150             
26151             this.syncValue();
26152             
26153             this.deferFocus();
26154         }
26155     },
26156  // private
26157     mozKeyPress : function(e){
26158         if(e.ctrlKey){
26159             var c = e.getCharCode(), cmd;
26160           
26161             if(c > 0){
26162                 c = String.fromCharCode(c).toLowerCase();
26163                 switch(c){
26164                     case 'b':
26165                         cmd = 'bold';
26166                         break;
26167                     case 'i':
26168                         cmd = 'italic';
26169                         break;
26170                     
26171                     case 'u':
26172                         cmd = 'underline';
26173                         break;
26174                     
26175                     case 'v':
26176                         this.cleanUpPaste.defer(100, this);
26177                         return;
26178                         
26179                 }
26180                 if(cmd){
26181                     this.win.focus();
26182                     this.execCmd(cmd);
26183                     this.deferFocus();
26184                     e.preventDefault();
26185                 }
26186                 
26187             }
26188         }
26189     },
26190
26191     // private
26192     fixKeys : function(){ // load time branching for fastest keydown performance
26193         if(Roo.isIE){
26194             return function(e){
26195                 var k = e.getKey(), r;
26196                 if(k == e.TAB){
26197                     e.stopEvent();
26198                     r = this.doc.selection.createRange();
26199                     if(r){
26200                         r.collapse(true);
26201                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26202                         this.deferFocus();
26203                     }
26204                     return;
26205                 }
26206                 
26207                 if(k == e.ENTER){
26208                     r = this.doc.selection.createRange();
26209                     if(r){
26210                         var target = r.parentElement();
26211                         if(!target || target.tagName.toLowerCase() != 'li'){
26212                             e.stopEvent();
26213                             r.pasteHTML('<br />');
26214                             r.collapse(false);
26215                             r.select();
26216                         }
26217                     }
26218                 }
26219                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26220                     this.cleanUpPaste.defer(100, this);
26221                     return;
26222                 }
26223                 
26224                 
26225             };
26226         }else if(Roo.isOpera){
26227             return function(e){
26228                 var k = e.getKey();
26229                 if(k == e.TAB){
26230                     e.stopEvent();
26231                     this.win.focus();
26232                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26233                     this.deferFocus();
26234                 }
26235                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26236                     this.cleanUpPaste.defer(100, this);
26237                     return;
26238                 }
26239                 
26240             };
26241         }else if(Roo.isSafari){
26242             return function(e){
26243                 var k = e.getKey();
26244                 
26245                 if(k == e.TAB){
26246                     e.stopEvent();
26247                     this.execCmd('InsertText','\t');
26248                     this.deferFocus();
26249                     return;
26250                 }
26251                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26252                     this.cleanUpPaste.defer(100, this);
26253                     return;
26254                 }
26255                 
26256              };
26257         }
26258     }(),
26259     
26260     getAllAncestors: function()
26261     {
26262         var p = this.getSelectedNode();
26263         var a = [];
26264         if (!p) {
26265             a.push(p); // push blank onto stack..
26266             p = this.getParentElement();
26267         }
26268         
26269         
26270         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26271             a.push(p);
26272             p = p.parentNode;
26273         }
26274         a.push(this.doc.body);
26275         return a;
26276     },
26277     lastSel : false,
26278     lastSelNode : false,
26279     
26280     
26281     getSelection : function() 
26282     {
26283         this.assignDocWin();
26284         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26285     },
26286     
26287     getSelectedNode: function() 
26288     {
26289         // this may only work on Gecko!!!
26290         
26291         // should we cache this!!!!
26292         
26293         
26294         
26295          
26296         var range = this.createRange(this.getSelection()).cloneRange();
26297         
26298         if (Roo.isIE) {
26299             var parent = range.parentElement();
26300             while (true) {
26301                 var testRange = range.duplicate();
26302                 testRange.moveToElementText(parent);
26303                 if (testRange.inRange(range)) {
26304                     break;
26305                 }
26306                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26307                     break;
26308                 }
26309                 parent = parent.parentElement;
26310             }
26311             return parent;
26312         }
26313         
26314         // is ancestor a text element.
26315         var ac =  range.commonAncestorContainer;
26316         if (ac.nodeType == 3) {
26317             ac = ac.parentNode;
26318         }
26319         
26320         var ar = ac.childNodes;
26321          
26322         var nodes = [];
26323         var other_nodes = [];
26324         var has_other_nodes = false;
26325         for (var i=0;i<ar.length;i++) {
26326             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26327                 continue;
26328             }
26329             // fullly contained node.
26330             
26331             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26332                 nodes.push(ar[i]);
26333                 continue;
26334             }
26335             
26336             // probably selected..
26337             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26338                 other_nodes.push(ar[i]);
26339                 continue;
26340             }
26341             // outer..
26342             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26343                 continue;
26344             }
26345             
26346             
26347             has_other_nodes = true;
26348         }
26349         if (!nodes.length && other_nodes.length) {
26350             nodes= other_nodes;
26351         }
26352         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26353             return false;
26354         }
26355         
26356         return nodes[0];
26357     },
26358     createRange: function(sel)
26359     {
26360         // this has strange effects when using with 
26361         // top toolbar - not sure if it's a great idea.
26362         //this.editor.contentWindow.focus();
26363         if (typeof sel != "undefined") {
26364             try {
26365                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26366             } catch(e) {
26367                 return this.doc.createRange();
26368             }
26369         } else {
26370             return this.doc.createRange();
26371         }
26372     },
26373     getParentElement: function()
26374     {
26375         
26376         this.assignDocWin();
26377         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26378         
26379         var range = this.createRange(sel);
26380          
26381         try {
26382             var p = range.commonAncestorContainer;
26383             while (p.nodeType == 3) { // text node
26384                 p = p.parentNode;
26385             }
26386             return p;
26387         } catch (e) {
26388             return null;
26389         }
26390     
26391     },
26392     /***
26393      *
26394      * Range intersection.. the hard stuff...
26395      *  '-1' = before
26396      *  '0' = hits..
26397      *  '1' = after.
26398      *         [ -- selected range --- ]
26399      *   [fail]                        [fail]
26400      *
26401      *    basically..
26402      *      if end is before start or  hits it. fail.
26403      *      if start is after end or hits it fail.
26404      *
26405      *   if either hits (but other is outside. - then it's not 
26406      *   
26407      *    
26408      **/
26409     
26410     
26411     // @see http://www.thismuchiknow.co.uk/?p=64.
26412     rangeIntersectsNode : function(range, node)
26413     {
26414         var nodeRange = node.ownerDocument.createRange();
26415         try {
26416             nodeRange.selectNode(node);
26417         } catch (e) {
26418             nodeRange.selectNodeContents(node);
26419         }
26420     
26421         var rangeStartRange = range.cloneRange();
26422         rangeStartRange.collapse(true);
26423     
26424         var rangeEndRange = range.cloneRange();
26425         rangeEndRange.collapse(false);
26426     
26427         var nodeStartRange = nodeRange.cloneRange();
26428         nodeStartRange.collapse(true);
26429     
26430         var nodeEndRange = nodeRange.cloneRange();
26431         nodeEndRange.collapse(false);
26432     
26433         return rangeStartRange.compareBoundaryPoints(
26434                  Range.START_TO_START, nodeEndRange) == -1 &&
26435                rangeEndRange.compareBoundaryPoints(
26436                  Range.START_TO_START, nodeStartRange) == 1;
26437         
26438          
26439     },
26440     rangeCompareNode : function(range, node)
26441     {
26442         var nodeRange = node.ownerDocument.createRange();
26443         try {
26444             nodeRange.selectNode(node);
26445         } catch (e) {
26446             nodeRange.selectNodeContents(node);
26447         }
26448         
26449         
26450         range.collapse(true);
26451     
26452         nodeRange.collapse(true);
26453      
26454         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26455         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26456          
26457         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26458         
26459         var nodeIsBefore   =  ss == 1;
26460         var nodeIsAfter    = ee == -1;
26461         
26462         if (nodeIsBefore && nodeIsAfter) {
26463             return 0; // outer
26464         }
26465         if (!nodeIsBefore && nodeIsAfter) {
26466             return 1; //right trailed.
26467         }
26468         
26469         if (nodeIsBefore && !nodeIsAfter) {
26470             return 2;  // left trailed.
26471         }
26472         // fully contined.
26473         return 3;
26474     },
26475
26476     // private? - in a new class?
26477     cleanUpPaste :  function()
26478     {
26479         // cleans up the whole document..
26480         Roo.log('cleanuppaste');
26481         
26482         this.cleanUpChildren(this.doc.body);
26483         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26484         if (clean != this.doc.body.innerHTML) {
26485             this.doc.body.innerHTML = clean;
26486         }
26487         
26488     },
26489     
26490     cleanWordChars : function(input) {// change the chars to hex code
26491         var he = Roo.HtmlEditorCore;
26492         
26493         var output = input;
26494         Roo.each(he.swapCodes, function(sw) { 
26495             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26496             
26497             output = output.replace(swapper, sw[1]);
26498         });
26499         
26500         return output;
26501     },
26502     
26503     
26504     cleanUpChildren : function (n)
26505     {
26506         if (!n.childNodes.length) {
26507             return;
26508         }
26509         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26510            this.cleanUpChild(n.childNodes[i]);
26511         }
26512     },
26513     
26514     
26515         
26516     
26517     cleanUpChild : function (node)
26518     {
26519         var ed = this;
26520         //console.log(node);
26521         if (node.nodeName == "#text") {
26522             // clean up silly Windows -- stuff?
26523             return; 
26524         }
26525         if (node.nodeName == "#comment") {
26526             node.parentNode.removeChild(node);
26527             // clean up silly Windows -- stuff?
26528             return; 
26529         }
26530         var lcname = node.tagName.toLowerCase();
26531         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26532         // whitelist of tags..
26533         
26534         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26535             // remove node.
26536             node.parentNode.removeChild(node);
26537             return;
26538             
26539         }
26540         
26541         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26542         
26543         // spans with no attributes - just remove them..
26544         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26545             remove_keep_children = true;
26546         }
26547         
26548         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26549         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26550         
26551         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26552         //    remove_keep_children = true;
26553         //}
26554         
26555         if (remove_keep_children) {
26556             this.cleanUpChildren(node);
26557             // inserts everything just before this node...
26558             while (node.childNodes.length) {
26559                 var cn = node.childNodes[0];
26560                 node.removeChild(cn);
26561                 node.parentNode.insertBefore(cn, node);
26562             }
26563             node.parentNode.removeChild(node);
26564             return;
26565         }
26566         
26567         if (!node.attributes || !node.attributes.length) {
26568             
26569           
26570             
26571             
26572             this.cleanUpChildren(node);
26573             return;
26574         }
26575         
26576         function cleanAttr(n,v)
26577         {
26578             
26579             if (v.match(/^\./) || v.match(/^\//)) {
26580                 return;
26581             }
26582             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26583                 return;
26584             }
26585             if (v.match(/^#/)) {
26586                 return;
26587             }
26588             if (v.match(/^\{/)) { // allow template editing.
26589                 return;
26590             }
26591 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26592             node.removeAttribute(n);
26593             
26594         }
26595         
26596         var cwhite = this.cwhite;
26597         var cblack = this.cblack;
26598             
26599         function cleanStyle(n,v)
26600         {
26601             if (v.match(/expression/)) { //XSS?? should we even bother..
26602                 node.removeAttribute(n);
26603                 return;
26604             }
26605             
26606             var parts = v.split(/;/);
26607             var clean = [];
26608             
26609             Roo.each(parts, function(p) {
26610                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26611                 if (!p.length) {
26612                     return true;
26613                 }
26614                 var l = p.split(':').shift().replace(/\s+/g,'');
26615                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26616                 
26617                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26618 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26619                     //node.removeAttribute(n);
26620                     return true;
26621                 }
26622                 //Roo.log()
26623                 // only allow 'c whitelisted system attributes'
26624                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26625 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26626                     //node.removeAttribute(n);
26627                     return true;
26628                 }
26629                 
26630                 
26631                  
26632                 
26633                 clean.push(p);
26634                 return true;
26635             });
26636             if (clean.length) { 
26637                 node.setAttribute(n, clean.join(';'));
26638             } else {
26639                 node.removeAttribute(n);
26640             }
26641             
26642         }
26643         
26644         
26645         for (var i = node.attributes.length-1; i > -1 ; i--) {
26646             var a = node.attributes[i];
26647             //console.log(a);
26648             
26649             if (a.name.toLowerCase().substr(0,2)=='on')  {
26650                 node.removeAttribute(a.name);
26651                 continue;
26652             }
26653             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26654                 node.removeAttribute(a.name);
26655                 continue;
26656             }
26657             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26658                 cleanAttr(a.name,a.value); // fixme..
26659                 continue;
26660             }
26661             if (a.name == 'style') {
26662                 cleanStyle(a.name,a.value);
26663                 continue;
26664             }
26665             /// clean up MS crap..
26666             // tecnically this should be a list of valid class'es..
26667             
26668             
26669             if (a.name == 'class') {
26670                 if (a.value.match(/^Mso/)) {
26671                     node.removeAttribute('class');
26672                 }
26673                 
26674                 if (a.value.match(/^body$/)) {
26675                     node.removeAttribute('class');
26676                 }
26677                 continue;
26678             }
26679             
26680             // style cleanup!?
26681             // class cleanup?
26682             
26683         }
26684         
26685         
26686         this.cleanUpChildren(node);
26687         
26688         
26689     },
26690     
26691     /**
26692      * Clean up MS wordisms...
26693      */
26694     cleanWord : function(node)
26695     {
26696         if (!node) {
26697             this.cleanWord(this.doc.body);
26698             return;
26699         }
26700         
26701         if(
26702                 node.nodeName == 'SPAN' &&
26703                 !node.hasAttributes() &&
26704                 node.childNodes.length == 1 &&
26705                 node.firstChild.nodeName == "#text"  
26706         ) {
26707             var textNode = node.firstChild;
26708             node.removeChild(textNode);
26709             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26710                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26711             }
26712             node.parentNode.insertBefore(textNode, node);
26713             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26714                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26715             }
26716             node.parentNode.removeChild(node);
26717         }
26718         
26719         if (node.nodeName == "#text") {
26720             // clean up silly Windows -- stuff?
26721             return; 
26722         }
26723         if (node.nodeName == "#comment") {
26724             node.parentNode.removeChild(node);
26725             // clean up silly Windows -- stuff?
26726             return; 
26727         }
26728         
26729         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26730             node.parentNode.removeChild(node);
26731             return;
26732         }
26733         //Roo.log(node.tagName);
26734         // remove - but keep children..
26735         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26736             //Roo.log('-- removed');
26737             while (node.childNodes.length) {
26738                 var cn = node.childNodes[0];
26739                 node.removeChild(cn);
26740                 node.parentNode.insertBefore(cn, node);
26741                 // move node to parent - and clean it..
26742                 this.cleanWord(cn);
26743             }
26744             node.parentNode.removeChild(node);
26745             /// no need to iterate chidlren = it's got none..
26746             //this.iterateChildren(node, this.cleanWord);
26747             return;
26748         }
26749         // clean styles
26750         if (node.className.length) {
26751             
26752             var cn = node.className.split(/\W+/);
26753             var cna = [];
26754             Roo.each(cn, function(cls) {
26755                 if (cls.match(/Mso[a-zA-Z]+/)) {
26756                     return;
26757                 }
26758                 cna.push(cls);
26759             });
26760             node.className = cna.length ? cna.join(' ') : '';
26761             if (!cna.length) {
26762                 node.removeAttribute("class");
26763             }
26764         }
26765         
26766         if (node.hasAttribute("lang")) {
26767             node.removeAttribute("lang");
26768         }
26769         
26770         if (node.hasAttribute("style")) {
26771             
26772             var styles = node.getAttribute("style").split(";");
26773             var nstyle = [];
26774             Roo.each(styles, function(s) {
26775                 if (!s.match(/:/)) {
26776                     return;
26777                 }
26778                 var kv = s.split(":");
26779                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26780                     return;
26781                 }
26782                 // what ever is left... we allow.
26783                 nstyle.push(s);
26784             });
26785             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26786             if (!nstyle.length) {
26787                 node.removeAttribute('style');
26788             }
26789         }
26790         this.iterateChildren(node, this.cleanWord);
26791         
26792         
26793         
26794     },
26795     /**
26796      * iterateChildren of a Node, calling fn each time, using this as the scole..
26797      * @param {DomNode} node node to iterate children of.
26798      * @param {Function} fn method of this class to call on each item.
26799      */
26800     iterateChildren : function(node, fn)
26801     {
26802         if (!node.childNodes.length) {
26803                 return;
26804         }
26805         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26806            fn.call(this, node.childNodes[i])
26807         }
26808     },
26809     
26810     
26811     /**
26812      * cleanTableWidths.
26813      *
26814      * Quite often pasting from word etc.. results in tables with column and widths.
26815      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26816      *
26817      */
26818     cleanTableWidths : function(node)
26819     {
26820          
26821          
26822         if (!node) {
26823             this.cleanTableWidths(this.doc.body);
26824             return;
26825         }
26826         
26827         // ignore list...
26828         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26829             return; 
26830         }
26831         Roo.log(node.tagName);
26832         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26833             this.iterateChildren(node, this.cleanTableWidths);
26834             return;
26835         }
26836         if (node.hasAttribute('width')) {
26837             node.removeAttribute('width');
26838         }
26839         
26840          
26841         if (node.hasAttribute("style")) {
26842             // pretty basic...
26843             
26844             var styles = node.getAttribute("style").split(";");
26845             var nstyle = [];
26846             Roo.each(styles, function(s) {
26847                 if (!s.match(/:/)) {
26848                     return;
26849                 }
26850                 var kv = s.split(":");
26851                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26852                     return;
26853                 }
26854                 // what ever is left... we allow.
26855                 nstyle.push(s);
26856             });
26857             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26858             if (!nstyle.length) {
26859                 node.removeAttribute('style');
26860             }
26861         }
26862         
26863         this.iterateChildren(node, this.cleanTableWidths);
26864         
26865         
26866     },
26867     
26868     
26869     
26870     
26871     domToHTML : function(currentElement, depth, nopadtext) {
26872         
26873         depth = depth || 0;
26874         nopadtext = nopadtext || false;
26875     
26876         if (!currentElement) {
26877             return this.domToHTML(this.doc.body);
26878         }
26879         
26880         //Roo.log(currentElement);
26881         var j;
26882         var allText = false;
26883         var nodeName = currentElement.nodeName;
26884         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26885         
26886         if  (nodeName == '#text') {
26887             
26888             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26889         }
26890         
26891         
26892         var ret = '';
26893         if (nodeName != 'BODY') {
26894              
26895             var i = 0;
26896             // Prints the node tagName, such as <A>, <IMG>, etc
26897             if (tagName) {
26898                 var attr = [];
26899                 for(i = 0; i < currentElement.attributes.length;i++) {
26900                     // quoting?
26901                     var aname = currentElement.attributes.item(i).name;
26902                     if (!currentElement.attributes.item(i).value.length) {
26903                         continue;
26904                     }
26905                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26906                 }
26907                 
26908                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26909             } 
26910             else {
26911                 
26912                 // eack
26913             }
26914         } else {
26915             tagName = false;
26916         }
26917         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26918             return ret;
26919         }
26920         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26921             nopadtext = true;
26922         }
26923         
26924         
26925         // Traverse the tree
26926         i = 0;
26927         var currentElementChild = currentElement.childNodes.item(i);
26928         var allText = true;
26929         var innerHTML  = '';
26930         lastnode = '';
26931         while (currentElementChild) {
26932             // Formatting code (indent the tree so it looks nice on the screen)
26933             var nopad = nopadtext;
26934             if (lastnode == 'SPAN') {
26935                 nopad  = true;
26936             }
26937             // text
26938             if  (currentElementChild.nodeName == '#text') {
26939                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26940                 toadd = nopadtext ? toadd : toadd.trim();
26941                 if (!nopad && toadd.length > 80) {
26942                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26943                 }
26944                 innerHTML  += toadd;
26945                 
26946                 i++;
26947                 currentElementChild = currentElement.childNodes.item(i);
26948                 lastNode = '';
26949                 continue;
26950             }
26951             allText = false;
26952             
26953             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26954                 
26955             // Recursively traverse the tree structure of the child node
26956             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26957             lastnode = currentElementChild.nodeName;
26958             i++;
26959             currentElementChild=currentElement.childNodes.item(i);
26960         }
26961         
26962         ret += innerHTML;
26963         
26964         if (!allText) {
26965                 // The remaining code is mostly for formatting the tree
26966             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26967         }
26968         
26969         
26970         if (tagName) {
26971             ret+= "</"+tagName+">";
26972         }
26973         return ret;
26974         
26975     },
26976         
26977     applyBlacklists : function()
26978     {
26979         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26980         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26981         
26982         this.white = [];
26983         this.black = [];
26984         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26985             if (b.indexOf(tag) > -1) {
26986                 return;
26987             }
26988             this.white.push(tag);
26989             
26990         }, this);
26991         
26992         Roo.each(w, function(tag) {
26993             if (b.indexOf(tag) > -1) {
26994                 return;
26995             }
26996             if (this.white.indexOf(tag) > -1) {
26997                 return;
26998             }
26999             this.white.push(tag);
27000             
27001         }, this);
27002         
27003         
27004         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27005             if (w.indexOf(tag) > -1) {
27006                 return;
27007             }
27008             this.black.push(tag);
27009             
27010         }, this);
27011         
27012         Roo.each(b, function(tag) {
27013             if (w.indexOf(tag) > -1) {
27014                 return;
27015             }
27016             if (this.black.indexOf(tag) > -1) {
27017                 return;
27018             }
27019             this.black.push(tag);
27020             
27021         }, this);
27022         
27023         
27024         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27025         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27026         
27027         this.cwhite = [];
27028         this.cblack = [];
27029         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27030             if (b.indexOf(tag) > -1) {
27031                 return;
27032             }
27033             this.cwhite.push(tag);
27034             
27035         }, this);
27036         
27037         Roo.each(w, function(tag) {
27038             if (b.indexOf(tag) > -1) {
27039                 return;
27040             }
27041             if (this.cwhite.indexOf(tag) > -1) {
27042                 return;
27043             }
27044             this.cwhite.push(tag);
27045             
27046         }, this);
27047         
27048         
27049         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27050             if (w.indexOf(tag) > -1) {
27051                 return;
27052             }
27053             this.cblack.push(tag);
27054             
27055         }, this);
27056         
27057         Roo.each(b, function(tag) {
27058             if (w.indexOf(tag) > -1) {
27059                 return;
27060             }
27061             if (this.cblack.indexOf(tag) > -1) {
27062                 return;
27063             }
27064             this.cblack.push(tag);
27065             
27066         }, this);
27067     },
27068     
27069     setStylesheets : function(stylesheets)
27070     {
27071         if(typeof(stylesheets) == 'string'){
27072             Roo.get(this.iframe.contentDocument.head).createChild({
27073                 tag : 'link',
27074                 rel : 'stylesheet',
27075                 type : 'text/css',
27076                 href : stylesheets
27077             });
27078             
27079             return;
27080         }
27081         var _this = this;
27082      
27083         Roo.each(stylesheets, function(s) {
27084             if(!s.length){
27085                 return;
27086             }
27087             
27088             Roo.get(_this.iframe.contentDocument.head).createChild({
27089                 tag : 'link',
27090                 rel : 'stylesheet',
27091                 type : 'text/css',
27092                 href : s
27093             });
27094         });
27095
27096         
27097     },
27098     
27099     removeStylesheets : function()
27100     {
27101         var _this = this;
27102         
27103         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27104             s.remove();
27105         });
27106     },
27107     
27108     setStyle : function(style)
27109     {
27110         Roo.get(this.iframe.contentDocument.head).createChild({
27111             tag : 'style',
27112             type : 'text/css',
27113             html : style
27114         });
27115
27116         return;
27117     }
27118     
27119     // hide stuff that is not compatible
27120     /**
27121      * @event blur
27122      * @hide
27123      */
27124     /**
27125      * @event change
27126      * @hide
27127      */
27128     /**
27129      * @event focus
27130      * @hide
27131      */
27132     /**
27133      * @event specialkey
27134      * @hide
27135      */
27136     /**
27137      * @cfg {String} fieldClass @hide
27138      */
27139     /**
27140      * @cfg {String} focusClass @hide
27141      */
27142     /**
27143      * @cfg {String} autoCreate @hide
27144      */
27145     /**
27146      * @cfg {String} inputType @hide
27147      */
27148     /**
27149      * @cfg {String} invalidClass @hide
27150      */
27151     /**
27152      * @cfg {String} invalidText @hide
27153      */
27154     /**
27155      * @cfg {String} msgFx @hide
27156      */
27157     /**
27158      * @cfg {String} validateOnBlur @hide
27159      */
27160 });
27161
27162 Roo.HtmlEditorCore.white = [
27163         'area', 'br', 'img', 'input', 'hr', 'wbr',
27164         
27165        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27166        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27167        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27168        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27169        'table',   'ul',         'xmp', 
27170        
27171        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27172       'thead',   'tr', 
27173      
27174       'dir', 'menu', 'ol', 'ul', 'dl',
27175        
27176       'embed',  'object'
27177 ];
27178
27179
27180 Roo.HtmlEditorCore.black = [
27181     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27182         'applet', // 
27183         'base',   'basefont', 'bgsound', 'blink',  'body', 
27184         'frame',  'frameset', 'head',    'html',   'ilayer', 
27185         'iframe', 'layer',  'link',     'meta',    'object',   
27186         'script', 'style' ,'title',  'xml' // clean later..
27187 ];
27188 Roo.HtmlEditorCore.clean = [
27189     'script', 'style', 'title', 'xml'
27190 ];
27191 Roo.HtmlEditorCore.remove = [
27192     'font'
27193 ];
27194 // attributes..
27195
27196 Roo.HtmlEditorCore.ablack = [
27197     'on'
27198 ];
27199     
27200 Roo.HtmlEditorCore.aclean = [ 
27201     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27202 ];
27203
27204 // protocols..
27205 Roo.HtmlEditorCore.pwhite= [
27206         'http',  'https',  'mailto'
27207 ];
27208
27209 // white listed style attributes.
27210 Roo.HtmlEditorCore.cwhite= [
27211       //  'text-align', /// default is to allow most things..
27212       
27213          
27214 //        'font-size'//??
27215 ];
27216
27217 // black listed style attributes.
27218 Roo.HtmlEditorCore.cblack= [
27219       //  'font-size' -- this can be set by the project 
27220 ];
27221
27222
27223 Roo.HtmlEditorCore.swapCodes   =[ 
27224     [    8211, "&#8211;" ], 
27225     [    8212, "&#8212;" ], 
27226     [    8216,  "'" ],  
27227     [    8217, "'" ],  
27228     [    8220, '"' ],  
27229     [    8221, '"' ],  
27230     [    8226, "*" ],  
27231     [    8230, "..." ]
27232 ]; 
27233
27234     /*
27235  * - LGPL
27236  *
27237  * HtmlEditor
27238  * 
27239  */
27240
27241 /**
27242  * @class Roo.bootstrap.HtmlEditor
27243  * @extends Roo.bootstrap.TextArea
27244  * Bootstrap HtmlEditor class
27245
27246  * @constructor
27247  * Create a new HtmlEditor
27248  * @param {Object} config The config object
27249  */
27250
27251 Roo.bootstrap.HtmlEditor = function(config){
27252     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27253     if (!this.toolbars) {
27254         this.toolbars = [];
27255     }
27256     
27257     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27258     this.addEvents({
27259             /**
27260              * @event initialize
27261              * Fires when the editor is fully initialized (including the iframe)
27262              * @param {HtmlEditor} this
27263              */
27264             initialize: true,
27265             /**
27266              * @event activate
27267              * Fires when the editor is first receives the focus. Any insertion must wait
27268              * until after this event.
27269              * @param {HtmlEditor} this
27270              */
27271             activate: true,
27272              /**
27273              * @event beforesync
27274              * Fires before the textarea is updated with content from the editor iframe. Return false
27275              * to cancel the sync.
27276              * @param {HtmlEditor} this
27277              * @param {String} html
27278              */
27279             beforesync: true,
27280              /**
27281              * @event beforepush
27282              * Fires before the iframe editor is updated with content from the textarea. Return false
27283              * to cancel the push.
27284              * @param {HtmlEditor} this
27285              * @param {String} html
27286              */
27287             beforepush: true,
27288              /**
27289              * @event sync
27290              * Fires when the textarea is updated with content from the editor iframe.
27291              * @param {HtmlEditor} this
27292              * @param {String} html
27293              */
27294             sync: true,
27295              /**
27296              * @event push
27297              * Fires when the iframe editor is updated with content from the textarea.
27298              * @param {HtmlEditor} this
27299              * @param {String} html
27300              */
27301             push: true,
27302              /**
27303              * @event editmodechange
27304              * Fires when the editor switches edit modes
27305              * @param {HtmlEditor} this
27306              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27307              */
27308             editmodechange: true,
27309             /**
27310              * @event editorevent
27311              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27312              * @param {HtmlEditor} this
27313              */
27314             editorevent: true,
27315             /**
27316              * @event firstfocus
27317              * Fires when on first focus - needed by toolbars..
27318              * @param {HtmlEditor} this
27319              */
27320             firstfocus: true,
27321             /**
27322              * @event autosave
27323              * Auto save the htmlEditor value as a file into Events
27324              * @param {HtmlEditor} this
27325              */
27326             autosave: true,
27327             /**
27328              * @event savedpreview
27329              * preview the saved version of htmlEditor
27330              * @param {HtmlEditor} this
27331              */
27332             savedpreview: true
27333         });
27334 };
27335
27336
27337 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27338     
27339     
27340       /**
27341      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27342      */
27343     toolbars : false,
27344     
27345      /**
27346     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27347     */
27348     btns : [],
27349    
27350      /**
27351      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27352      *                        Roo.resizable.
27353      */
27354     resizable : false,
27355      /**
27356      * @cfg {Number} height (in pixels)
27357      */   
27358     height: 300,
27359    /**
27360      * @cfg {Number} width (in pixels)
27361      */   
27362     width: false,
27363     
27364     /**
27365      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27366      * 
27367      */
27368     stylesheets: false,
27369     
27370     // id of frame..
27371     frameId: false,
27372     
27373     // private properties
27374     validationEvent : false,
27375     deferHeight: true,
27376     initialized : false,
27377     activated : false,
27378     
27379     onFocus : Roo.emptyFn,
27380     iframePad:3,
27381     hideMode:'offsets',
27382     
27383     tbContainer : false,
27384     
27385     bodyCls : '',
27386     
27387     toolbarContainer :function() {
27388         return this.wrap.select('.x-html-editor-tb',true).first();
27389     },
27390
27391     /**
27392      * Protected method that will not generally be called directly. It
27393      * is called when the editor creates its toolbar. Override this method if you need to
27394      * add custom toolbar buttons.
27395      * @param {HtmlEditor} editor
27396      */
27397     createToolbar : function(){
27398         Roo.log('renewing');
27399         Roo.log("create toolbars");
27400         
27401         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27402         this.toolbars[0].render(this.toolbarContainer());
27403         
27404         return;
27405         
27406 //        if (!editor.toolbars || !editor.toolbars.length) {
27407 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27408 //        }
27409 //        
27410 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27411 //            editor.toolbars[i] = Roo.factory(
27412 //                    typeof(editor.toolbars[i]) == 'string' ?
27413 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27414 //                Roo.bootstrap.HtmlEditor);
27415 //            editor.toolbars[i].init(editor);
27416 //        }
27417     },
27418
27419      
27420     // private
27421     onRender : function(ct, position)
27422     {
27423        // Roo.log("Call onRender: " + this.xtype);
27424         var _t = this;
27425         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27426       
27427         this.wrap = this.inputEl().wrap({
27428             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27429         });
27430         
27431         this.editorcore.onRender(ct, position);
27432          
27433         if (this.resizable) {
27434             this.resizeEl = new Roo.Resizable(this.wrap, {
27435                 pinned : true,
27436                 wrap: true,
27437                 dynamic : true,
27438                 minHeight : this.height,
27439                 height: this.height,
27440                 handles : this.resizable,
27441                 width: this.width,
27442                 listeners : {
27443                     resize : function(r, w, h) {
27444                         _t.onResize(w,h); // -something
27445                     }
27446                 }
27447             });
27448             
27449         }
27450         this.createToolbar(this);
27451        
27452         
27453         if(!this.width && this.resizable){
27454             this.setSize(this.wrap.getSize());
27455         }
27456         if (this.resizeEl) {
27457             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27458             // should trigger onReize..
27459         }
27460         
27461     },
27462
27463     // private
27464     onResize : function(w, h)
27465     {
27466         Roo.log('resize: ' +w + ',' + h );
27467         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27468         var ew = false;
27469         var eh = false;
27470         
27471         if(this.inputEl() ){
27472             if(typeof w == 'number'){
27473                 var aw = w - this.wrap.getFrameWidth('lr');
27474                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27475                 ew = aw;
27476             }
27477             if(typeof h == 'number'){
27478                  var tbh = -11;  // fixme it needs to tool bar size!
27479                 for (var i =0; i < this.toolbars.length;i++) {
27480                     // fixme - ask toolbars for heights?
27481                     tbh += this.toolbars[i].el.getHeight();
27482                     //if (this.toolbars[i].footer) {
27483                     //    tbh += this.toolbars[i].footer.el.getHeight();
27484                     //}
27485                 }
27486               
27487                 
27488                 
27489                 
27490                 
27491                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27492                 ah -= 5; // knock a few pixes off for look..
27493                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27494                 var eh = ah;
27495             }
27496         }
27497         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27498         this.editorcore.onResize(ew,eh);
27499         
27500     },
27501
27502     /**
27503      * Toggles the editor between standard and source edit mode.
27504      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27505      */
27506     toggleSourceEdit : function(sourceEditMode)
27507     {
27508         this.editorcore.toggleSourceEdit(sourceEditMode);
27509         
27510         if(this.editorcore.sourceEditMode){
27511             Roo.log('editor - showing textarea');
27512             
27513 //            Roo.log('in');
27514 //            Roo.log(this.syncValue());
27515             this.syncValue();
27516             this.inputEl().removeClass(['hide', 'x-hidden']);
27517             this.inputEl().dom.removeAttribute('tabIndex');
27518             this.inputEl().focus();
27519         }else{
27520             Roo.log('editor - hiding textarea');
27521 //            Roo.log('out')
27522 //            Roo.log(this.pushValue()); 
27523             this.pushValue();
27524             
27525             this.inputEl().addClass(['hide', 'x-hidden']);
27526             this.inputEl().dom.setAttribute('tabIndex', -1);
27527             //this.deferFocus();
27528         }
27529          
27530         if(this.resizable){
27531             this.setSize(this.wrap.getSize());
27532         }
27533         
27534         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27535     },
27536  
27537     // private (for BoxComponent)
27538     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27539
27540     // private (for BoxComponent)
27541     getResizeEl : function(){
27542         return this.wrap;
27543     },
27544
27545     // private (for BoxComponent)
27546     getPositionEl : function(){
27547         return this.wrap;
27548     },
27549
27550     // private
27551     initEvents : function(){
27552         this.originalValue = this.getValue();
27553     },
27554
27555 //    /**
27556 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27557 //     * @method
27558 //     */
27559 //    markInvalid : Roo.emptyFn,
27560 //    /**
27561 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27562 //     * @method
27563 //     */
27564 //    clearInvalid : Roo.emptyFn,
27565
27566     setValue : function(v){
27567         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27568         this.editorcore.pushValue();
27569     },
27570
27571      
27572     // private
27573     deferFocus : function(){
27574         this.focus.defer(10, this);
27575     },
27576
27577     // doc'ed in Field
27578     focus : function(){
27579         this.editorcore.focus();
27580         
27581     },
27582       
27583
27584     // private
27585     onDestroy : function(){
27586         
27587         
27588         
27589         if(this.rendered){
27590             
27591             for (var i =0; i < this.toolbars.length;i++) {
27592                 // fixme - ask toolbars for heights?
27593                 this.toolbars[i].onDestroy();
27594             }
27595             
27596             this.wrap.dom.innerHTML = '';
27597             this.wrap.remove();
27598         }
27599     },
27600
27601     // private
27602     onFirstFocus : function(){
27603         //Roo.log("onFirstFocus");
27604         this.editorcore.onFirstFocus();
27605          for (var i =0; i < this.toolbars.length;i++) {
27606             this.toolbars[i].onFirstFocus();
27607         }
27608         
27609     },
27610     
27611     // private
27612     syncValue : function()
27613     {   
27614         this.editorcore.syncValue();
27615     },
27616     
27617     pushValue : function()
27618     {   
27619         this.editorcore.pushValue();
27620     }
27621      
27622     
27623     // hide stuff that is not compatible
27624     /**
27625      * @event blur
27626      * @hide
27627      */
27628     /**
27629      * @event change
27630      * @hide
27631      */
27632     /**
27633      * @event focus
27634      * @hide
27635      */
27636     /**
27637      * @event specialkey
27638      * @hide
27639      */
27640     /**
27641      * @cfg {String} fieldClass @hide
27642      */
27643     /**
27644      * @cfg {String} focusClass @hide
27645      */
27646     /**
27647      * @cfg {String} autoCreate @hide
27648      */
27649     /**
27650      * @cfg {String} inputType @hide
27651      */
27652      
27653     /**
27654      * @cfg {String} invalidText @hide
27655      */
27656     /**
27657      * @cfg {String} msgFx @hide
27658      */
27659     /**
27660      * @cfg {String} validateOnBlur @hide
27661      */
27662 });
27663  
27664     
27665    
27666    
27667    
27668       
27669 Roo.namespace('Roo.bootstrap.htmleditor');
27670 /**
27671  * @class Roo.bootstrap.HtmlEditorToolbar1
27672  * Basic Toolbar
27673  * 
27674  * @example
27675  * Usage:
27676  *
27677  new Roo.bootstrap.HtmlEditor({
27678     ....
27679     toolbars : [
27680         new Roo.bootstrap.HtmlEditorToolbar1({
27681             disable : { fonts: 1 , format: 1, ..., ... , ...],
27682             btns : [ .... ]
27683         })
27684     }
27685      
27686  * 
27687  * @cfg {Object} disable List of elements to disable..
27688  * @cfg {Array} btns List of additional buttons.
27689  * 
27690  * 
27691  * NEEDS Extra CSS? 
27692  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27693  */
27694  
27695 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27696 {
27697     
27698     Roo.apply(this, config);
27699     
27700     // default disabled, based on 'good practice'..
27701     this.disable = this.disable || {};
27702     Roo.applyIf(this.disable, {
27703         fontSize : true,
27704         colors : true,
27705         specialElements : true
27706     });
27707     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27708     
27709     this.editor = config.editor;
27710     this.editorcore = config.editor.editorcore;
27711     
27712     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27713     
27714     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27715     // dont call parent... till later.
27716 }
27717 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27718      
27719     bar : true,
27720     
27721     editor : false,
27722     editorcore : false,
27723     
27724     
27725     formats : [
27726         "p" ,  
27727         "h1","h2","h3","h4","h5","h6", 
27728         "pre", "code", 
27729         "abbr", "acronym", "address", "cite", "samp", "var",
27730         'div','span'
27731     ],
27732     
27733     onRender : function(ct, position)
27734     {
27735        // Roo.log("Call onRender: " + this.xtype);
27736         
27737        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27738        Roo.log(this.el);
27739        this.el.dom.style.marginBottom = '0';
27740        var _this = this;
27741        var editorcore = this.editorcore;
27742        var editor= this.editor;
27743        
27744        var children = [];
27745        var btn = function(id,cmd , toggle, handler, html){
27746        
27747             var  event = toggle ? 'toggle' : 'click';
27748        
27749             var a = {
27750                 size : 'sm',
27751                 xtype: 'Button',
27752                 xns: Roo.bootstrap,
27753                 //glyphicon : id,
27754                 fa: id,
27755                 cmd : id || cmd,
27756                 enableToggle:toggle !== false,
27757                 html : html || '',
27758                 pressed : toggle ? false : null,
27759                 listeners : {}
27760             };
27761             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27762                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27763             };
27764             children.push(a);
27765             return a;
27766        }
27767        
27768     //    var cb_box = function...
27769         
27770         var style = {
27771                 xtype: 'Button',
27772                 size : 'sm',
27773                 xns: Roo.bootstrap,
27774                 fa : 'font',
27775                 //html : 'submit'
27776                 menu : {
27777                     xtype: 'Menu',
27778                     xns: Roo.bootstrap,
27779                     items:  []
27780                 }
27781         };
27782         Roo.each(this.formats, function(f) {
27783             style.menu.items.push({
27784                 xtype :'MenuItem',
27785                 xns: Roo.bootstrap,
27786                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27787                 tagname : f,
27788                 listeners : {
27789                     click : function()
27790                     {
27791                         editorcore.insertTag(this.tagname);
27792                         editor.focus();
27793                     }
27794                 }
27795                 
27796             });
27797         });
27798         children.push(style);   
27799         
27800         btn('bold',false,true);
27801         btn('italic',false,true);
27802         btn('align-left', 'justifyleft',true);
27803         btn('align-center', 'justifycenter',true);
27804         btn('align-right' , 'justifyright',true);
27805         btn('link', false, false, function(btn) {
27806             //Roo.log("create link?");
27807             var url = prompt(this.createLinkText, this.defaultLinkValue);
27808             if(url && url != 'http:/'+'/'){
27809                 this.editorcore.relayCmd('createlink', url);
27810             }
27811         }),
27812         btn('list','insertunorderedlist',true);
27813         btn('pencil', false,true, function(btn){
27814                 Roo.log(this);
27815                 this.toggleSourceEdit(btn.pressed);
27816         });
27817         
27818         if (this.editor.btns.length > 0) {
27819             for (var i = 0; i<this.editor.btns.length; i++) {
27820                 children.push(this.editor.btns[i]);
27821             }
27822         }
27823         
27824         /*
27825         var cog = {
27826                 xtype: 'Button',
27827                 size : 'sm',
27828                 xns: Roo.bootstrap,
27829                 glyphicon : 'cog',
27830                 //html : 'submit'
27831                 menu : {
27832                     xtype: 'Menu',
27833                     xns: Roo.bootstrap,
27834                     items:  []
27835                 }
27836         };
27837         
27838         cog.menu.items.push({
27839             xtype :'MenuItem',
27840             xns: Roo.bootstrap,
27841             html : Clean styles,
27842             tagname : f,
27843             listeners : {
27844                 click : function()
27845                 {
27846                     editorcore.insertTag(this.tagname);
27847                     editor.focus();
27848                 }
27849             }
27850             
27851         });
27852        */
27853         
27854          
27855        this.xtype = 'NavSimplebar';
27856         
27857         for(var i=0;i< children.length;i++) {
27858             
27859             this.buttons.add(this.addxtypeChild(children[i]));
27860             
27861         }
27862         
27863         editor.on('editorevent', this.updateToolbar, this);
27864     },
27865     onBtnClick : function(id)
27866     {
27867        this.editorcore.relayCmd(id);
27868        this.editorcore.focus();
27869     },
27870     
27871     /**
27872      * Protected method that will not generally be called directly. It triggers
27873      * a toolbar update by reading the markup state of the current selection in the editor.
27874      */
27875     updateToolbar: function(){
27876
27877         if(!this.editorcore.activated){
27878             this.editor.onFirstFocus(); // is this neeed?
27879             return;
27880         }
27881
27882         var btns = this.buttons; 
27883         var doc = this.editorcore.doc;
27884         btns.get('bold').setActive(doc.queryCommandState('bold'));
27885         btns.get('italic').setActive(doc.queryCommandState('italic'));
27886         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27887         
27888         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27889         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27890         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27891         
27892         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27893         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27894          /*
27895         
27896         var ans = this.editorcore.getAllAncestors();
27897         if (this.formatCombo) {
27898             
27899             
27900             var store = this.formatCombo.store;
27901             this.formatCombo.setValue("");
27902             for (var i =0; i < ans.length;i++) {
27903                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27904                     // select it..
27905                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27906                     break;
27907                 }
27908             }
27909         }
27910         
27911         
27912         
27913         // hides menus... - so this cant be on a menu...
27914         Roo.bootstrap.MenuMgr.hideAll();
27915         */
27916         Roo.bootstrap.MenuMgr.hideAll();
27917         //this.editorsyncValue();
27918     },
27919     onFirstFocus: function() {
27920         this.buttons.each(function(item){
27921            item.enable();
27922         });
27923     },
27924     toggleSourceEdit : function(sourceEditMode){
27925         
27926           
27927         if(sourceEditMode){
27928             Roo.log("disabling buttons");
27929            this.buttons.each( function(item){
27930                 if(item.cmd != 'pencil'){
27931                     item.disable();
27932                 }
27933             });
27934           
27935         }else{
27936             Roo.log("enabling buttons");
27937             if(this.editorcore.initialized){
27938                 this.buttons.each( function(item){
27939                     item.enable();
27940                 });
27941             }
27942             
27943         }
27944         Roo.log("calling toggole on editor");
27945         // tell the editor that it's been pressed..
27946         this.editor.toggleSourceEdit(sourceEditMode);
27947        
27948     }
27949 });
27950
27951
27952
27953
27954  
27955 /*
27956  * - LGPL
27957  */
27958
27959 /**
27960  * @class Roo.bootstrap.Markdown
27961  * @extends Roo.bootstrap.TextArea
27962  * Bootstrap Showdown editable area
27963  * @cfg {string} content
27964  * 
27965  * @constructor
27966  * Create a new Showdown
27967  */
27968
27969 Roo.bootstrap.Markdown = function(config){
27970     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27971    
27972 };
27973
27974 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27975     
27976     editing :false,
27977     
27978     initEvents : function()
27979     {
27980         
27981         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27982         this.markdownEl = this.el.createChild({
27983             cls : 'roo-markdown-area'
27984         });
27985         this.inputEl().addClass('d-none');
27986         if (this.getValue() == '') {
27987             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27988             
27989         } else {
27990             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27991         }
27992         this.markdownEl.on('click', this.toggleTextEdit, this);
27993         this.on('blur', this.toggleTextEdit, this);
27994         this.on('specialkey', this.resizeTextArea, this);
27995     },
27996     
27997     toggleTextEdit : function()
27998     {
27999         var sh = this.markdownEl.getHeight();
28000         this.inputEl().addClass('d-none');
28001         this.markdownEl.addClass('d-none');
28002         if (!this.editing) {
28003             // show editor?
28004             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28005             this.inputEl().removeClass('d-none');
28006             this.inputEl().focus();
28007             this.editing = true;
28008             return;
28009         }
28010         // show showdown...
28011         this.updateMarkdown();
28012         this.markdownEl.removeClass('d-none');
28013         this.editing = false;
28014         return;
28015     },
28016     updateMarkdown : function()
28017     {
28018         if (this.getValue() == '') {
28019             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28020             return;
28021         }
28022  
28023         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28024     },
28025     
28026     resizeTextArea: function () {
28027         
28028         var sh = 100;
28029         Roo.log([sh, this.getValue().split("\n").length * 30]);
28030         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28031     },
28032     setValue : function(val)
28033     {
28034         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28035         if (!this.editing) {
28036             this.updateMarkdown();
28037         }
28038         
28039     },
28040     focus : function()
28041     {
28042         if (!this.editing) {
28043             this.toggleTextEdit();
28044         }
28045         
28046     }
28047
28048
28049 });/*
28050  * Based on:
28051  * Ext JS Library 1.1.1
28052  * Copyright(c) 2006-2007, Ext JS, LLC.
28053  *
28054  * Originally Released Under LGPL - original licence link has changed is not relivant.
28055  *
28056  * Fork - LGPL
28057  * <script type="text/javascript">
28058  */
28059  
28060 /**
28061  * @class Roo.bootstrap.PagingToolbar
28062  * @extends Roo.bootstrap.NavSimplebar
28063  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28064  * @constructor
28065  * Create a new PagingToolbar
28066  * @param {Object} config The config object
28067  * @param {Roo.data.Store} store
28068  */
28069 Roo.bootstrap.PagingToolbar = function(config)
28070 {
28071     // old args format still supported... - xtype is prefered..
28072         // created from xtype...
28073     
28074     this.ds = config.dataSource;
28075     
28076     if (config.store && !this.ds) {
28077         this.store= Roo.factory(config.store, Roo.data);
28078         this.ds = this.store;
28079         this.ds.xmodule = this.xmodule || false;
28080     }
28081     
28082     this.toolbarItems = [];
28083     if (config.items) {
28084         this.toolbarItems = config.items;
28085     }
28086     
28087     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28088     
28089     this.cursor = 0;
28090     
28091     if (this.ds) { 
28092         this.bind(this.ds);
28093     }
28094     
28095     if (Roo.bootstrap.version == 4) {
28096         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28097     } else {
28098         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28099     }
28100     
28101 };
28102
28103 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28104     /**
28105      * @cfg {Roo.data.Store} dataSource
28106      * The underlying data store providing the paged data
28107      */
28108     /**
28109      * @cfg {String/HTMLElement/Element} container
28110      * container The id or element that will contain the toolbar
28111      */
28112     /**
28113      * @cfg {Boolean} displayInfo
28114      * True to display the displayMsg (defaults to false)
28115      */
28116     /**
28117      * @cfg {Number} pageSize
28118      * The number of records to display per page (defaults to 20)
28119      */
28120     pageSize: 20,
28121     /**
28122      * @cfg {String} displayMsg
28123      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28124      */
28125     displayMsg : 'Displaying {0} - {1} of {2}',
28126     /**
28127      * @cfg {String} emptyMsg
28128      * The message to display when no records are found (defaults to "No data to display")
28129      */
28130     emptyMsg : 'No data to display',
28131     /**
28132      * Customizable piece of the default paging text (defaults to "Page")
28133      * @type String
28134      */
28135     beforePageText : "Page",
28136     /**
28137      * Customizable piece of the default paging text (defaults to "of %0")
28138      * @type String
28139      */
28140     afterPageText : "of {0}",
28141     /**
28142      * Customizable piece of the default paging text (defaults to "First Page")
28143      * @type String
28144      */
28145     firstText : "First Page",
28146     /**
28147      * Customizable piece of the default paging text (defaults to "Previous Page")
28148      * @type String
28149      */
28150     prevText : "Previous Page",
28151     /**
28152      * Customizable piece of the default paging text (defaults to "Next Page")
28153      * @type String
28154      */
28155     nextText : "Next Page",
28156     /**
28157      * Customizable piece of the default paging text (defaults to "Last Page")
28158      * @type String
28159      */
28160     lastText : "Last Page",
28161     /**
28162      * Customizable piece of the default paging text (defaults to "Refresh")
28163      * @type String
28164      */
28165     refreshText : "Refresh",
28166
28167     buttons : false,
28168     // private
28169     onRender : function(ct, position) 
28170     {
28171         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28172         this.navgroup.parentId = this.id;
28173         this.navgroup.onRender(this.el, null);
28174         // add the buttons to the navgroup
28175         
28176         if(this.displayInfo){
28177             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28178             this.displayEl = this.el.select('.x-paging-info', true).first();
28179 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28180 //            this.displayEl = navel.el.select('span',true).first();
28181         }
28182         
28183         var _this = this;
28184         
28185         if(this.buttons){
28186             Roo.each(_this.buttons, function(e){ // this might need to use render????
28187                Roo.factory(e).render(_this.el);
28188             });
28189         }
28190             
28191         Roo.each(_this.toolbarItems, function(e) {
28192             _this.navgroup.addItem(e);
28193         });
28194         
28195         
28196         this.first = this.navgroup.addItem({
28197             tooltip: this.firstText,
28198             cls: "prev btn-outline-secondary",
28199             html : ' <i class="fa fa-step-backward"></i>',
28200             disabled: true,
28201             preventDefault: true,
28202             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28203         });
28204         
28205         this.prev =  this.navgroup.addItem({
28206             tooltip: this.prevText,
28207             cls: "prev btn-outline-secondary",
28208             html : ' <i class="fa fa-backward"></i>',
28209             disabled: true,
28210             preventDefault: true,
28211             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28212         });
28213     //this.addSeparator();
28214         
28215         
28216         var field = this.navgroup.addItem( {
28217             tagtype : 'span',
28218             cls : 'x-paging-position  btn-outline-secondary',
28219              disabled: true,
28220             html : this.beforePageText  +
28221                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28222                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28223          } ); //?? escaped?
28224         
28225         this.field = field.el.select('input', true).first();
28226         this.field.on("keydown", this.onPagingKeydown, this);
28227         this.field.on("focus", function(){this.dom.select();});
28228     
28229     
28230         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28231         //this.field.setHeight(18);
28232         //this.addSeparator();
28233         this.next = this.navgroup.addItem({
28234             tooltip: this.nextText,
28235             cls: "next btn-outline-secondary",
28236             html : ' <i class="fa fa-forward"></i>',
28237             disabled: true,
28238             preventDefault: true,
28239             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28240         });
28241         this.last = this.navgroup.addItem({
28242             tooltip: this.lastText,
28243             html : ' <i class="fa fa-step-forward"></i>',
28244             cls: "next btn-outline-secondary",
28245             disabled: true,
28246             preventDefault: true,
28247             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28248         });
28249     //this.addSeparator();
28250         this.loading = this.navgroup.addItem({
28251             tooltip: this.refreshText,
28252             cls: "btn-outline-secondary",
28253             html : ' <i class="fa fa-refresh"></i>',
28254             preventDefault: true,
28255             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28256         });
28257         
28258     },
28259
28260     // private
28261     updateInfo : function(){
28262         if(this.displayEl){
28263             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28264             var msg = count == 0 ?
28265                 this.emptyMsg :
28266                 String.format(
28267                     this.displayMsg,
28268                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28269                 );
28270             this.displayEl.update(msg);
28271         }
28272     },
28273
28274     // private
28275     onLoad : function(ds, r, o)
28276     {
28277         this.cursor = o.params && o.params.start ? o.params.start : 0;
28278         
28279         var d = this.getPageData(),
28280             ap = d.activePage,
28281             ps = d.pages;
28282         
28283         
28284         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28285         this.field.dom.value = ap;
28286         this.first.setDisabled(ap == 1);
28287         this.prev.setDisabled(ap == 1);
28288         this.next.setDisabled(ap == ps);
28289         this.last.setDisabled(ap == ps);
28290         this.loading.enable();
28291         this.updateInfo();
28292     },
28293
28294     // private
28295     getPageData : function(){
28296         var total = this.ds.getTotalCount();
28297         return {
28298             total : total,
28299             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28300             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28301         };
28302     },
28303
28304     // private
28305     onLoadError : function(){
28306         this.loading.enable();
28307     },
28308
28309     // private
28310     onPagingKeydown : function(e){
28311         var k = e.getKey();
28312         var d = this.getPageData();
28313         if(k == e.RETURN){
28314             var v = this.field.dom.value, pageNum;
28315             if(!v || isNaN(pageNum = parseInt(v, 10))){
28316                 this.field.dom.value = d.activePage;
28317                 return;
28318             }
28319             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28320             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28321             e.stopEvent();
28322         }
28323         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))
28324         {
28325           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28326           this.field.dom.value = pageNum;
28327           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28328           e.stopEvent();
28329         }
28330         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28331         {
28332           var v = this.field.dom.value, pageNum; 
28333           var increment = (e.shiftKey) ? 10 : 1;
28334           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28335                 increment *= -1;
28336           }
28337           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28338             this.field.dom.value = d.activePage;
28339             return;
28340           }
28341           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28342           {
28343             this.field.dom.value = parseInt(v, 10) + increment;
28344             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28345             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28346           }
28347           e.stopEvent();
28348         }
28349     },
28350
28351     // private
28352     beforeLoad : function(){
28353         if(this.loading){
28354             this.loading.disable();
28355         }
28356     },
28357
28358     // private
28359     onClick : function(which){
28360         
28361         var ds = this.ds;
28362         if (!ds) {
28363             return;
28364         }
28365         
28366         switch(which){
28367             case "first":
28368                 ds.load({params:{start: 0, limit: this.pageSize}});
28369             break;
28370             case "prev":
28371                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28372             break;
28373             case "next":
28374                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28375             break;
28376             case "last":
28377                 var total = ds.getTotalCount();
28378                 var extra = total % this.pageSize;
28379                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28380                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28381             break;
28382             case "refresh":
28383                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28384             break;
28385         }
28386     },
28387
28388     /**
28389      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28390      * @param {Roo.data.Store} store The data store to unbind
28391      */
28392     unbind : function(ds){
28393         ds.un("beforeload", this.beforeLoad, this);
28394         ds.un("load", this.onLoad, this);
28395         ds.un("loadexception", this.onLoadError, this);
28396         ds.un("remove", this.updateInfo, this);
28397         ds.un("add", this.updateInfo, this);
28398         this.ds = undefined;
28399     },
28400
28401     /**
28402      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28403      * @param {Roo.data.Store} store The data store to bind
28404      */
28405     bind : function(ds){
28406         ds.on("beforeload", this.beforeLoad, this);
28407         ds.on("load", this.onLoad, this);
28408         ds.on("loadexception", this.onLoadError, this);
28409         ds.on("remove", this.updateInfo, this);
28410         ds.on("add", this.updateInfo, this);
28411         this.ds = ds;
28412     }
28413 });/*
28414  * - LGPL
28415  *
28416  * element
28417  * 
28418  */
28419
28420 /**
28421  * @class Roo.bootstrap.MessageBar
28422  * @extends Roo.bootstrap.Component
28423  * Bootstrap MessageBar class
28424  * @cfg {String} html contents of the MessageBar
28425  * @cfg {String} weight (info | success | warning | danger) default info
28426  * @cfg {String} beforeClass insert the bar before the given class
28427  * @cfg {Boolean} closable (true | false) default false
28428  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28429  * 
28430  * @constructor
28431  * Create a new Element
28432  * @param {Object} config The config object
28433  */
28434
28435 Roo.bootstrap.MessageBar = function(config){
28436     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28437 };
28438
28439 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28440     
28441     html: '',
28442     weight: 'info',
28443     closable: false,
28444     fixed: false,
28445     beforeClass: 'bootstrap-sticky-wrap',
28446     
28447     getAutoCreate : function(){
28448         
28449         var cfg = {
28450             tag: 'div',
28451             cls: 'alert alert-dismissable alert-' + this.weight,
28452             cn: [
28453                 {
28454                     tag: 'span',
28455                     cls: 'message',
28456                     html: this.html || ''
28457                 }
28458             ]
28459         };
28460         
28461         if(this.fixed){
28462             cfg.cls += ' alert-messages-fixed';
28463         }
28464         
28465         if(this.closable){
28466             cfg.cn.push({
28467                 tag: 'button',
28468                 cls: 'close',
28469                 html: 'x'
28470             });
28471         }
28472         
28473         return cfg;
28474     },
28475     
28476     onRender : function(ct, position)
28477     {
28478         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28479         
28480         if(!this.el){
28481             var cfg = Roo.apply({},  this.getAutoCreate());
28482             cfg.id = Roo.id();
28483             
28484             if (this.cls) {
28485                 cfg.cls += ' ' + this.cls;
28486             }
28487             if (this.style) {
28488                 cfg.style = this.style;
28489             }
28490             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28491             
28492             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28493         }
28494         
28495         this.el.select('>button.close').on('click', this.hide, this);
28496         
28497     },
28498     
28499     show : function()
28500     {
28501         if (!this.rendered) {
28502             this.render();
28503         }
28504         
28505         this.el.show();
28506         
28507         this.fireEvent('show', this);
28508         
28509     },
28510     
28511     hide : function()
28512     {
28513         if (!this.rendered) {
28514             this.render();
28515         }
28516         
28517         this.el.hide();
28518         
28519         this.fireEvent('hide', this);
28520     },
28521     
28522     update : function()
28523     {
28524 //        var e = this.el.dom.firstChild;
28525 //        
28526 //        if(this.closable){
28527 //            e = e.nextSibling;
28528 //        }
28529 //        
28530 //        e.data = this.html || '';
28531
28532         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28533     }
28534    
28535 });
28536
28537  
28538
28539      /*
28540  * - LGPL
28541  *
28542  * Graph
28543  * 
28544  */
28545
28546
28547 /**
28548  * @class Roo.bootstrap.Graph
28549  * @extends Roo.bootstrap.Component
28550  * Bootstrap Graph class
28551 > Prameters
28552  -sm {number} sm 4
28553  -md {number} md 5
28554  @cfg {String} graphtype  bar | vbar | pie
28555  @cfg {number} g_x coodinator | centre x (pie)
28556  @cfg {number} g_y coodinator | centre y (pie)
28557  @cfg {number} g_r radius (pie)
28558  @cfg {number} g_height height of the chart (respected by all elements in the set)
28559  @cfg {number} g_width width of the chart (respected by all elements in the set)
28560  @cfg {Object} title The title of the chart
28561     
28562  -{Array}  values
28563  -opts (object) options for the chart 
28564      o {
28565      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28566      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28567      o vgutter (number)
28568      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.
28569      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28570      o to
28571      o stretch (boolean)
28572      o }
28573  -opts (object) options for the pie
28574      o{
28575      o cut
28576      o startAngle (number)
28577      o endAngle (number)
28578      } 
28579  *
28580  * @constructor
28581  * Create a new Input
28582  * @param {Object} config The config object
28583  */
28584
28585 Roo.bootstrap.Graph = function(config){
28586     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28587     
28588     this.addEvents({
28589         // img events
28590         /**
28591          * @event click
28592          * The img click event for the img.
28593          * @param {Roo.EventObject} e
28594          */
28595         "click" : true
28596     });
28597 };
28598
28599 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28600     
28601     sm: 4,
28602     md: 5,
28603     graphtype: 'bar',
28604     g_height: 250,
28605     g_width: 400,
28606     g_x: 50,
28607     g_y: 50,
28608     g_r: 30,
28609     opts:{
28610         //g_colors: this.colors,
28611         g_type: 'soft',
28612         g_gutter: '20%'
28613
28614     },
28615     title : false,
28616
28617     getAutoCreate : function(){
28618         
28619         var cfg = {
28620             tag: 'div',
28621             html : null
28622         };
28623         
28624         
28625         return  cfg;
28626     },
28627
28628     onRender : function(ct,position){
28629         
28630         
28631         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28632         
28633         if (typeof(Raphael) == 'undefined') {
28634             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28635             return;
28636         }
28637         
28638         this.raphael = Raphael(this.el.dom);
28639         
28640                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28641                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28642                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28643                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28644                 /*
28645                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28646                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28647                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28648                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28649                 
28650                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28651                 r.barchart(330, 10, 300, 220, data1);
28652                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28653                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28654                 */
28655                 
28656                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28657                 // r.barchart(30, 30, 560, 250,  xdata, {
28658                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28659                 //     axis : "0 0 1 1",
28660                 //     axisxlabels :  xdata
28661                 //     //yvalues : cols,
28662                    
28663                 // });
28664 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28665 //        
28666 //        this.load(null,xdata,{
28667 //                axis : "0 0 1 1",
28668 //                axisxlabels :  xdata
28669 //                });
28670
28671     },
28672
28673     load : function(graphtype,xdata,opts)
28674     {
28675         this.raphael.clear();
28676         if(!graphtype) {
28677             graphtype = this.graphtype;
28678         }
28679         if(!opts){
28680             opts = this.opts;
28681         }
28682         var r = this.raphael,
28683             fin = function () {
28684                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28685             },
28686             fout = function () {
28687                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28688             },
28689             pfin = function() {
28690                 this.sector.stop();
28691                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28692
28693                 if (this.label) {
28694                     this.label[0].stop();
28695                     this.label[0].attr({ r: 7.5 });
28696                     this.label[1].attr({ "font-weight": 800 });
28697                 }
28698             },
28699             pfout = function() {
28700                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28701
28702                 if (this.label) {
28703                     this.label[0].animate({ r: 5 }, 500, "bounce");
28704                     this.label[1].attr({ "font-weight": 400 });
28705                 }
28706             };
28707
28708         switch(graphtype){
28709             case 'bar':
28710                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28711                 break;
28712             case 'hbar':
28713                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28714                 break;
28715             case 'pie':
28716 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28717 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28718 //            
28719                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28720                 
28721                 break;
28722
28723         }
28724         
28725         if(this.title){
28726             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28727         }
28728         
28729     },
28730     
28731     setTitle: function(o)
28732     {
28733         this.title = o;
28734     },
28735     
28736     initEvents: function() {
28737         
28738         if(!this.href){
28739             this.el.on('click', this.onClick, this);
28740         }
28741     },
28742     
28743     onClick : function(e)
28744     {
28745         Roo.log('img onclick');
28746         this.fireEvent('click', this, e);
28747     }
28748    
28749 });
28750
28751  
28752 /*
28753  * - LGPL
28754  *
28755  * numberBox
28756  * 
28757  */
28758 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28759
28760 /**
28761  * @class Roo.bootstrap.dash.NumberBox
28762  * @extends Roo.bootstrap.Component
28763  * Bootstrap NumberBox class
28764  * @cfg {String} headline Box headline
28765  * @cfg {String} content Box content
28766  * @cfg {String} icon Box icon
28767  * @cfg {String} footer Footer text
28768  * @cfg {String} fhref Footer href
28769  * 
28770  * @constructor
28771  * Create a new NumberBox
28772  * @param {Object} config The config object
28773  */
28774
28775
28776 Roo.bootstrap.dash.NumberBox = function(config){
28777     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28778     
28779 };
28780
28781 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28782     
28783     headline : '',
28784     content : '',
28785     icon : '',
28786     footer : '',
28787     fhref : '',
28788     ficon : '',
28789     
28790     getAutoCreate : function(){
28791         
28792         var cfg = {
28793             tag : 'div',
28794             cls : 'small-box ',
28795             cn : [
28796                 {
28797                     tag : 'div',
28798                     cls : 'inner',
28799                     cn :[
28800                         {
28801                             tag : 'h3',
28802                             cls : 'roo-headline',
28803                             html : this.headline
28804                         },
28805                         {
28806                             tag : 'p',
28807                             cls : 'roo-content',
28808                             html : this.content
28809                         }
28810                     ]
28811                 }
28812             ]
28813         };
28814         
28815         if(this.icon){
28816             cfg.cn.push({
28817                 tag : 'div',
28818                 cls : 'icon',
28819                 cn :[
28820                     {
28821                         tag : 'i',
28822                         cls : 'ion ' + this.icon
28823                     }
28824                 ]
28825             });
28826         }
28827         
28828         if(this.footer){
28829             var footer = {
28830                 tag : 'a',
28831                 cls : 'small-box-footer',
28832                 href : this.fhref || '#',
28833                 html : this.footer
28834             };
28835             
28836             cfg.cn.push(footer);
28837             
28838         }
28839         
28840         return  cfg;
28841     },
28842
28843     onRender : function(ct,position){
28844         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28845
28846
28847        
28848                 
28849     },
28850
28851     setHeadline: function (value)
28852     {
28853         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28854     },
28855     
28856     setFooter: function (value, href)
28857     {
28858         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28859         
28860         if(href){
28861             this.el.select('a.small-box-footer',true).first().attr('href', href);
28862         }
28863         
28864     },
28865
28866     setContent: function (value)
28867     {
28868         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28869     },
28870
28871     initEvents: function() 
28872     {   
28873         
28874     }
28875     
28876 });
28877
28878  
28879 /*
28880  * - LGPL
28881  *
28882  * TabBox
28883  * 
28884  */
28885 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28886
28887 /**
28888  * @class Roo.bootstrap.dash.TabBox
28889  * @extends Roo.bootstrap.Component
28890  * Bootstrap TabBox class
28891  * @cfg {String} title Title of the TabBox
28892  * @cfg {String} icon Icon of the TabBox
28893  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28894  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28895  * 
28896  * @constructor
28897  * Create a new TabBox
28898  * @param {Object} config The config object
28899  */
28900
28901
28902 Roo.bootstrap.dash.TabBox = function(config){
28903     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28904     this.addEvents({
28905         // raw events
28906         /**
28907          * @event addpane
28908          * When a pane is added
28909          * @param {Roo.bootstrap.dash.TabPane} pane
28910          */
28911         "addpane" : true,
28912         /**
28913          * @event activatepane
28914          * When a pane is activated
28915          * @param {Roo.bootstrap.dash.TabPane} pane
28916          */
28917         "activatepane" : true
28918         
28919          
28920     });
28921     
28922     this.panes = [];
28923 };
28924
28925 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28926
28927     title : '',
28928     icon : false,
28929     showtabs : true,
28930     tabScrollable : false,
28931     
28932     getChildContainer : function()
28933     {
28934         return this.el.select('.tab-content', true).first();
28935     },
28936     
28937     getAutoCreate : function(){
28938         
28939         var header = {
28940             tag: 'li',
28941             cls: 'pull-left header',
28942             html: this.title,
28943             cn : []
28944         };
28945         
28946         if(this.icon){
28947             header.cn.push({
28948                 tag: 'i',
28949                 cls: 'fa ' + this.icon
28950             });
28951         }
28952         
28953         var h = {
28954             tag: 'ul',
28955             cls: 'nav nav-tabs pull-right',
28956             cn: [
28957                 header
28958             ]
28959         };
28960         
28961         if(this.tabScrollable){
28962             h = {
28963                 tag: 'div',
28964                 cls: 'tab-header',
28965                 cn: [
28966                     {
28967                         tag: 'ul',
28968                         cls: 'nav nav-tabs pull-right',
28969                         cn: [
28970                             header
28971                         ]
28972                     }
28973                 ]
28974             };
28975         }
28976         
28977         var cfg = {
28978             tag: 'div',
28979             cls: 'nav-tabs-custom',
28980             cn: [
28981                 h,
28982                 {
28983                     tag: 'div',
28984                     cls: 'tab-content no-padding',
28985                     cn: []
28986                 }
28987             ]
28988         };
28989
28990         return  cfg;
28991     },
28992     initEvents : function()
28993     {
28994         //Roo.log('add add pane handler');
28995         this.on('addpane', this.onAddPane, this);
28996     },
28997      /**
28998      * Updates the box title
28999      * @param {String} html to set the title to.
29000      */
29001     setTitle : function(value)
29002     {
29003         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29004     },
29005     onAddPane : function(pane)
29006     {
29007         this.panes.push(pane);
29008         //Roo.log('addpane');
29009         //Roo.log(pane);
29010         // tabs are rendere left to right..
29011         if(!this.showtabs){
29012             return;
29013         }
29014         
29015         var ctr = this.el.select('.nav-tabs', true).first();
29016          
29017          
29018         var existing = ctr.select('.nav-tab',true);
29019         var qty = existing.getCount();;
29020         
29021         
29022         var tab = ctr.createChild({
29023             tag : 'li',
29024             cls : 'nav-tab' + (qty ? '' : ' active'),
29025             cn : [
29026                 {
29027                     tag : 'a',
29028                     href:'#',
29029                     html : pane.title
29030                 }
29031             ]
29032         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29033         pane.tab = tab;
29034         
29035         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29036         if (!qty) {
29037             pane.el.addClass('active');
29038         }
29039         
29040                 
29041     },
29042     onTabClick : function(ev,un,ob,pane)
29043     {
29044         //Roo.log('tab - prev default');
29045         ev.preventDefault();
29046         
29047         
29048         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29049         pane.tab.addClass('active');
29050         //Roo.log(pane.title);
29051         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29052         // technically we should have a deactivate event.. but maybe add later.
29053         // and it should not de-activate the selected tab...
29054         this.fireEvent('activatepane', pane);
29055         pane.el.addClass('active');
29056         pane.fireEvent('activate');
29057         
29058         
29059     },
29060     
29061     getActivePane : function()
29062     {
29063         var r = false;
29064         Roo.each(this.panes, function(p) {
29065             if(p.el.hasClass('active')){
29066                 r = p;
29067                 return false;
29068             }
29069             
29070             return;
29071         });
29072         
29073         return r;
29074     }
29075     
29076     
29077 });
29078
29079  
29080 /*
29081  * - LGPL
29082  *
29083  * Tab pane
29084  * 
29085  */
29086 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29087 /**
29088  * @class Roo.bootstrap.TabPane
29089  * @extends Roo.bootstrap.Component
29090  * Bootstrap TabPane class
29091  * @cfg {Boolean} active (false | true) Default false
29092  * @cfg {String} title title of panel
29093
29094  * 
29095  * @constructor
29096  * Create a new TabPane
29097  * @param {Object} config The config object
29098  */
29099
29100 Roo.bootstrap.dash.TabPane = function(config){
29101     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29102     
29103     this.addEvents({
29104         // raw events
29105         /**
29106          * @event activate
29107          * When a pane is activated
29108          * @param {Roo.bootstrap.dash.TabPane} pane
29109          */
29110         "activate" : true
29111          
29112     });
29113 };
29114
29115 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29116     
29117     active : false,
29118     title : '',
29119     
29120     // the tabBox that this is attached to.
29121     tab : false,
29122      
29123     getAutoCreate : function() 
29124     {
29125         var cfg = {
29126             tag: 'div',
29127             cls: 'tab-pane'
29128         };
29129         
29130         if(this.active){
29131             cfg.cls += ' active';
29132         }
29133         
29134         return cfg;
29135     },
29136     initEvents  : function()
29137     {
29138         //Roo.log('trigger add pane handler');
29139         this.parent().fireEvent('addpane', this)
29140     },
29141     
29142      /**
29143      * Updates the tab title 
29144      * @param {String} html to set the title to.
29145      */
29146     setTitle: function(str)
29147     {
29148         if (!this.tab) {
29149             return;
29150         }
29151         this.title = str;
29152         this.tab.select('a', true).first().dom.innerHTML = str;
29153         
29154     }
29155     
29156     
29157     
29158 });
29159
29160  
29161
29162
29163  /*
29164  * - LGPL
29165  *
29166  * menu
29167  * 
29168  */
29169 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29170
29171 /**
29172  * @class Roo.bootstrap.menu.Menu
29173  * @extends Roo.bootstrap.Component
29174  * Bootstrap Menu class - container for Menu
29175  * @cfg {String} html Text of the menu
29176  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29177  * @cfg {String} icon Font awesome icon
29178  * @cfg {String} pos Menu align to (top | bottom) default bottom
29179  * 
29180  * 
29181  * @constructor
29182  * Create a new Menu
29183  * @param {Object} config The config object
29184  */
29185
29186
29187 Roo.bootstrap.menu.Menu = function(config){
29188     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29189     
29190     this.addEvents({
29191         /**
29192          * @event beforeshow
29193          * Fires before this menu is displayed
29194          * @param {Roo.bootstrap.menu.Menu} this
29195          */
29196         beforeshow : true,
29197         /**
29198          * @event beforehide
29199          * Fires before this menu is hidden
29200          * @param {Roo.bootstrap.menu.Menu} this
29201          */
29202         beforehide : true,
29203         /**
29204          * @event show
29205          * Fires after this menu is displayed
29206          * @param {Roo.bootstrap.menu.Menu} this
29207          */
29208         show : true,
29209         /**
29210          * @event hide
29211          * Fires after this menu is hidden
29212          * @param {Roo.bootstrap.menu.Menu} this
29213          */
29214         hide : true,
29215         /**
29216          * @event click
29217          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29218          * @param {Roo.bootstrap.menu.Menu} this
29219          * @param {Roo.EventObject} e
29220          */
29221         click : true
29222     });
29223     
29224 };
29225
29226 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29227     
29228     submenu : false,
29229     html : '',
29230     weight : 'default',
29231     icon : false,
29232     pos : 'bottom',
29233     
29234     
29235     getChildContainer : function() {
29236         if(this.isSubMenu){
29237             return this.el;
29238         }
29239         
29240         return this.el.select('ul.dropdown-menu', true).first();  
29241     },
29242     
29243     getAutoCreate : function()
29244     {
29245         var text = [
29246             {
29247                 tag : 'span',
29248                 cls : 'roo-menu-text',
29249                 html : this.html
29250             }
29251         ];
29252         
29253         if(this.icon){
29254             text.unshift({
29255                 tag : 'i',
29256                 cls : 'fa ' + this.icon
29257             })
29258         }
29259         
29260         
29261         var cfg = {
29262             tag : 'div',
29263             cls : 'btn-group',
29264             cn : [
29265                 {
29266                     tag : 'button',
29267                     cls : 'dropdown-button btn btn-' + this.weight,
29268                     cn : text
29269                 },
29270                 {
29271                     tag : 'button',
29272                     cls : 'dropdown-toggle btn btn-' + this.weight,
29273                     cn : [
29274                         {
29275                             tag : 'span',
29276                             cls : 'caret'
29277                         }
29278                     ]
29279                 },
29280                 {
29281                     tag : 'ul',
29282                     cls : 'dropdown-menu'
29283                 }
29284             ]
29285             
29286         };
29287         
29288         if(this.pos == 'top'){
29289             cfg.cls += ' dropup';
29290         }
29291         
29292         if(this.isSubMenu){
29293             cfg = {
29294                 tag : 'ul',
29295                 cls : 'dropdown-menu'
29296             }
29297         }
29298         
29299         return cfg;
29300     },
29301     
29302     onRender : function(ct, position)
29303     {
29304         this.isSubMenu = ct.hasClass('dropdown-submenu');
29305         
29306         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29307     },
29308     
29309     initEvents : function() 
29310     {
29311         if(this.isSubMenu){
29312             return;
29313         }
29314         
29315         this.hidden = true;
29316         
29317         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29318         this.triggerEl.on('click', this.onTriggerPress, this);
29319         
29320         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29321         this.buttonEl.on('click', this.onClick, this);
29322         
29323     },
29324     
29325     list : function()
29326     {
29327         if(this.isSubMenu){
29328             return this.el;
29329         }
29330         
29331         return this.el.select('ul.dropdown-menu', true).first();
29332     },
29333     
29334     onClick : function(e)
29335     {
29336         this.fireEvent("click", this, e);
29337     },
29338     
29339     onTriggerPress  : function(e)
29340     {   
29341         if (this.isVisible()) {
29342             this.hide();
29343         } else {
29344             this.show();
29345         }
29346     },
29347     
29348     isVisible : function(){
29349         return !this.hidden;
29350     },
29351     
29352     show : function()
29353     {
29354         this.fireEvent("beforeshow", this);
29355         
29356         this.hidden = false;
29357         this.el.addClass('open');
29358         
29359         Roo.get(document).on("mouseup", this.onMouseUp, this);
29360         
29361         this.fireEvent("show", this);
29362         
29363         
29364     },
29365     
29366     hide : function()
29367     {
29368         this.fireEvent("beforehide", this);
29369         
29370         this.hidden = true;
29371         this.el.removeClass('open');
29372         
29373         Roo.get(document).un("mouseup", this.onMouseUp);
29374         
29375         this.fireEvent("hide", this);
29376     },
29377     
29378     onMouseUp : function()
29379     {
29380         this.hide();
29381     }
29382     
29383 });
29384
29385  
29386  /*
29387  * - LGPL
29388  *
29389  * menu item
29390  * 
29391  */
29392 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29393
29394 /**
29395  * @class Roo.bootstrap.menu.Item
29396  * @extends Roo.bootstrap.Component
29397  * Bootstrap MenuItem class
29398  * @cfg {Boolean} submenu (true | false) default false
29399  * @cfg {String} html text of the item
29400  * @cfg {String} href the link
29401  * @cfg {Boolean} disable (true | false) default false
29402  * @cfg {Boolean} preventDefault (true | false) default true
29403  * @cfg {String} icon Font awesome icon
29404  * @cfg {String} pos Submenu align to (left | right) default right 
29405  * 
29406  * 
29407  * @constructor
29408  * Create a new Item
29409  * @param {Object} config The config object
29410  */
29411
29412
29413 Roo.bootstrap.menu.Item = function(config){
29414     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29415     this.addEvents({
29416         /**
29417          * @event mouseover
29418          * Fires when the mouse is hovering over this menu
29419          * @param {Roo.bootstrap.menu.Item} this
29420          * @param {Roo.EventObject} e
29421          */
29422         mouseover : true,
29423         /**
29424          * @event mouseout
29425          * Fires when the mouse exits this menu
29426          * @param {Roo.bootstrap.menu.Item} this
29427          * @param {Roo.EventObject} e
29428          */
29429         mouseout : true,
29430         // raw events
29431         /**
29432          * @event click
29433          * The raw click event for the entire grid.
29434          * @param {Roo.EventObject} e
29435          */
29436         click : true
29437     });
29438 };
29439
29440 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29441     
29442     submenu : false,
29443     href : '',
29444     html : '',
29445     preventDefault: true,
29446     disable : false,
29447     icon : false,
29448     pos : 'right',
29449     
29450     getAutoCreate : function()
29451     {
29452         var text = [
29453             {
29454                 tag : 'span',
29455                 cls : 'roo-menu-item-text',
29456                 html : this.html
29457             }
29458         ];
29459         
29460         if(this.icon){
29461             text.unshift({
29462                 tag : 'i',
29463                 cls : 'fa ' + this.icon
29464             })
29465         }
29466         
29467         var cfg = {
29468             tag : 'li',
29469             cn : [
29470                 {
29471                     tag : 'a',
29472                     href : this.href || '#',
29473                     cn : text
29474                 }
29475             ]
29476         };
29477         
29478         if(this.disable){
29479             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29480         }
29481         
29482         if(this.submenu){
29483             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29484             
29485             if(this.pos == 'left'){
29486                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29487             }
29488         }
29489         
29490         return cfg;
29491     },
29492     
29493     initEvents : function() 
29494     {
29495         this.el.on('mouseover', this.onMouseOver, this);
29496         this.el.on('mouseout', this.onMouseOut, this);
29497         
29498         this.el.select('a', true).first().on('click', this.onClick, this);
29499         
29500     },
29501     
29502     onClick : function(e)
29503     {
29504         if(this.preventDefault){
29505             e.preventDefault();
29506         }
29507         
29508         this.fireEvent("click", this, e);
29509     },
29510     
29511     onMouseOver : function(e)
29512     {
29513         if(this.submenu && this.pos == 'left'){
29514             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29515         }
29516         
29517         this.fireEvent("mouseover", this, e);
29518     },
29519     
29520     onMouseOut : function(e)
29521     {
29522         this.fireEvent("mouseout", this, e);
29523     }
29524 });
29525
29526  
29527
29528  /*
29529  * - LGPL
29530  *
29531  * menu separator
29532  * 
29533  */
29534 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29535
29536 /**
29537  * @class Roo.bootstrap.menu.Separator
29538  * @extends Roo.bootstrap.Component
29539  * Bootstrap Separator class
29540  * 
29541  * @constructor
29542  * Create a new Separator
29543  * @param {Object} config The config object
29544  */
29545
29546
29547 Roo.bootstrap.menu.Separator = function(config){
29548     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29549 };
29550
29551 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29552     
29553     getAutoCreate : function(){
29554         var cfg = {
29555             tag : 'li',
29556             cls: 'dropdown-divider divider'
29557         };
29558         
29559         return cfg;
29560     }
29561    
29562 });
29563
29564  
29565
29566  /*
29567  * - LGPL
29568  *
29569  * Tooltip
29570  * 
29571  */
29572
29573 /**
29574  * @class Roo.bootstrap.Tooltip
29575  * Bootstrap Tooltip class
29576  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29577  * to determine which dom element triggers the tooltip.
29578  * 
29579  * It needs to add support for additional attributes like tooltip-position
29580  * 
29581  * @constructor
29582  * Create a new Toolti
29583  * @param {Object} config The config object
29584  */
29585
29586 Roo.bootstrap.Tooltip = function(config){
29587     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29588     
29589     this.alignment = Roo.bootstrap.Tooltip.alignment;
29590     
29591     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29592         this.alignment = config.alignment;
29593     }
29594     
29595 };
29596
29597 Roo.apply(Roo.bootstrap.Tooltip, {
29598     /**
29599      * @function init initialize tooltip monitoring.
29600      * @static
29601      */
29602     currentEl : false,
29603     currentTip : false,
29604     currentRegion : false,
29605     
29606     //  init : delay?
29607     
29608     init : function()
29609     {
29610         Roo.get(document).on('mouseover', this.enter ,this);
29611         Roo.get(document).on('mouseout', this.leave, this);
29612          
29613         
29614         this.currentTip = new Roo.bootstrap.Tooltip();
29615     },
29616     
29617     enter : function(ev)
29618     {
29619         var dom = ev.getTarget();
29620         
29621         //Roo.log(['enter',dom]);
29622         var el = Roo.fly(dom);
29623         if (this.currentEl) {
29624             //Roo.log(dom);
29625             //Roo.log(this.currentEl);
29626             //Roo.log(this.currentEl.contains(dom));
29627             if (this.currentEl == el) {
29628                 return;
29629             }
29630             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29631                 return;
29632             }
29633
29634         }
29635         
29636         if (this.currentTip.el) {
29637             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29638         }    
29639         //Roo.log(ev);
29640         
29641         if(!el || el.dom == document){
29642             return;
29643         }
29644         
29645         var bindEl = el; 
29646         var pel = false;
29647         if (!el.attr('tooltip')) {
29648             pel = el.findParent("[tooltip]");
29649             if (pel) {
29650                 bindEl = Roo.get(pel);
29651             }
29652         }
29653         
29654        
29655         
29656         // you can not look for children, as if el is the body.. then everythign is the child..
29657         if (!pel && !el.attr('tooltip')) { //
29658             if (!el.select("[tooltip]").elements.length) {
29659                 return;
29660             }
29661             // is the mouse over this child...?
29662             bindEl = el.select("[tooltip]").first();
29663             var xy = ev.getXY();
29664             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29665                 //Roo.log("not in region.");
29666                 return;
29667             }
29668             //Roo.log("child element over..");
29669             
29670         }
29671         this.currentEl = el;
29672         this.currentTip.bind(bindEl);
29673         this.currentRegion = Roo.lib.Region.getRegion(dom);
29674         this.currentTip.enter();
29675         
29676     },
29677     leave : function(ev)
29678     {
29679         var dom = ev.getTarget();
29680         //Roo.log(['leave',dom]);
29681         if (!this.currentEl) {
29682             return;
29683         }
29684         
29685         
29686         if (dom != this.currentEl.dom) {
29687             return;
29688         }
29689         var xy = ev.getXY();
29690         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29691             return;
29692         }
29693         // only activate leave if mouse cursor is outside... bounding box..
29694         
29695         
29696         
29697         
29698         if (this.currentTip) {
29699             this.currentTip.leave();
29700         }
29701         //Roo.log('clear currentEl');
29702         this.currentEl = false;
29703         
29704         
29705     },
29706     alignment : {
29707         'left' : ['r-l', [-2,0], 'right'],
29708         'right' : ['l-r', [2,0], 'left'],
29709         'bottom' : ['t-b', [0,2], 'top'],
29710         'top' : [ 'b-t', [0,-2], 'bottom']
29711     }
29712     
29713 });
29714
29715
29716 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29717     
29718     
29719     bindEl : false,
29720     
29721     delay : null, // can be { show : 300 , hide: 500}
29722     
29723     timeout : null,
29724     
29725     hoverState : null, //???
29726     
29727     placement : 'bottom', 
29728     
29729     alignment : false,
29730     
29731     getAutoCreate : function(){
29732     
29733         var cfg = {
29734            cls : 'tooltip',   
29735            role : 'tooltip',
29736            cn : [
29737                 {
29738                     cls : 'tooltip-arrow arrow'
29739                 },
29740                 {
29741                     cls : 'tooltip-inner'
29742                 }
29743            ]
29744         };
29745         
29746         return cfg;
29747     },
29748     bind : function(el)
29749     {
29750         this.bindEl = el;
29751     },
29752     
29753     initEvents : function()
29754     {
29755         this.arrowEl = this.el.select('.arrow', true).first();
29756         this.innerEl = this.el.select('.tooltip-inner', true).first();
29757     },
29758     
29759     enter : function () {
29760        
29761         if (this.timeout != null) {
29762             clearTimeout(this.timeout);
29763         }
29764         
29765         this.hoverState = 'in';
29766          //Roo.log("enter - show");
29767         if (!this.delay || !this.delay.show) {
29768             this.show();
29769             return;
29770         }
29771         var _t = this;
29772         this.timeout = setTimeout(function () {
29773             if (_t.hoverState == 'in') {
29774                 _t.show();
29775             }
29776         }, this.delay.show);
29777     },
29778     leave : function()
29779     {
29780         clearTimeout(this.timeout);
29781     
29782         this.hoverState = 'out';
29783          if (!this.delay || !this.delay.hide) {
29784             this.hide();
29785             return;
29786         }
29787        
29788         var _t = this;
29789         this.timeout = setTimeout(function () {
29790             //Roo.log("leave - timeout");
29791             
29792             if (_t.hoverState == 'out') {
29793                 _t.hide();
29794                 Roo.bootstrap.Tooltip.currentEl = false;
29795             }
29796         }, delay);
29797     },
29798     
29799     show : function (msg)
29800     {
29801         if (!this.el) {
29802             this.render(document.body);
29803         }
29804         // set content.
29805         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29806         
29807         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29808         
29809         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29810         
29811         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29812                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29813         
29814         var placement = typeof this.placement == 'function' ?
29815             this.placement.call(this, this.el, on_el) :
29816             this.placement;
29817             
29818         var autoToken = /\s?auto?\s?/i;
29819         var autoPlace = autoToken.test(placement);
29820         if (autoPlace) {
29821             placement = placement.replace(autoToken, '') || 'top';
29822         }
29823         
29824         //this.el.detach()
29825         //this.el.setXY([0,0]);
29826         this.el.show();
29827         //this.el.dom.style.display='block';
29828         
29829         //this.el.appendTo(on_el);
29830         
29831         var p = this.getPosition();
29832         var box = this.el.getBox();
29833         
29834         if (autoPlace) {
29835             // fixme..
29836         }
29837         
29838         var align = this.alignment[placement];
29839         
29840         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29841         
29842         if(placement == 'top' || placement == 'bottom'){
29843             if(xy[0] < 0){
29844                 placement = 'right';
29845             }
29846             
29847             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29848                 placement = 'left';
29849             }
29850             
29851             var scroll = Roo.select('body', true).first().getScroll();
29852             
29853             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29854                 placement = 'top';
29855             }
29856             
29857             align = this.alignment[placement];
29858             
29859             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29860             
29861         }
29862         
29863         var elems = document.getElementsByTagName('div');
29864         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29865         for (var i = 0; i < elems.length; i++) {
29866           var zindex = Number.parseInt(
29867                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29868                 10
29869           );
29870           if (zindex > highest) {
29871             highest = zindex;
29872           }
29873         }
29874         
29875         
29876         
29877         this.el.dom.style.zIndex = highest;
29878         
29879         this.el.alignTo(this.bindEl, align[0],align[1]);
29880         //var arrow = this.el.select('.arrow',true).first();
29881         //arrow.set(align[2], 
29882         
29883         this.el.addClass(placement);
29884         this.el.addClass("bs-tooltip-"+ placement);
29885         
29886         this.el.addClass('in fade show');
29887         
29888         this.hoverState = null;
29889         
29890         if (this.el.hasClass('fade')) {
29891             // fade it?
29892         }
29893         
29894         
29895         
29896         
29897         
29898     },
29899     hide : function()
29900     {
29901          
29902         if (!this.el) {
29903             return;
29904         }
29905         //this.el.setXY([0,0]);
29906         this.el.removeClass(['show', 'in']);
29907         //this.el.hide();
29908         
29909     }
29910     
29911 });
29912  
29913
29914  /*
29915  * - LGPL
29916  *
29917  * Location Picker
29918  * 
29919  */
29920
29921 /**
29922  * @class Roo.bootstrap.LocationPicker
29923  * @extends Roo.bootstrap.Component
29924  * Bootstrap LocationPicker class
29925  * @cfg {Number} latitude Position when init default 0
29926  * @cfg {Number} longitude Position when init default 0
29927  * @cfg {Number} zoom default 15
29928  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29929  * @cfg {Boolean} mapTypeControl default false
29930  * @cfg {Boolean} disableDoubleClickZoom default false
29931  * @cfg {Boolean} scrollwheel default true
29932  * @cfg {Boolean} streetViewControl default false
29933  * @cfg {Number} radius default 0
29934  * @cfg {String} locationName
29935  * @cfg {Boolean} draggable default true
29936  * @cfg {Boolean} enableAutocomplete default false
29937  * @cfg {Boolean} enableReverseGeocode default true
29938  * @cfg {String} markerTitle
29939  * 
29940  * @constructor
29941  * Create a new LocationPicker
29942  * @param {Object} config The config object
29943  */
29944
29945
29946 Roo.bootstrap.LocationPicker = function(config){
29947     
29948     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29949     
29950     this.addEvents({
29951         /**
29952          * @event initial
29953          * Fires when the picker initialized.
29954          * @param {Roo.bootstrap.LocationPicker} this
29955          * @param {Google Location} location
29956          */
29957         initial : true,
29958         /**
29959          * @event positionchanged
29960          * Fires when the picker position changed.
29961          * @param {Roo.bootstrap.LocationPicker} this
29962          * @param {Google Location} location
29963          */
29964         positionchanged : true,
29965         /**
29966          * @event resize
29967          * Fires when the map resize.
29968          * @param {Roo.bootstrap.LocationPicker} this
29969          */
29970         resize : true,
29971         /**
29972          * @event show
29973          * Fires when the map show.
29974          * @param {Roo.bootstrap.LocationPicker} this
29975          */
29976         show : true,
29977         /**
29978          * @event hide
29979          * Fires when the map hide.
29980          * @param {Roo.bootstrap.LocationPicker} this
29981          */
29982         hide : true,
29983         /**
29984          * @event mapClick
29985          * Fires when click the map.
29986          * @param {Roo.bootstrap.LocationPicker} this
29987          * @param {Map event} e
29988          */
29989         mapClick : true,
29990         /**
29991          * @event mapRightClick
29992          * Fires when right click the map.
29993          * @param {Roo.bootstrap.LocationPicker} this
29994          * @param {Map event} e
29995          */
29996         mapRightClick : true,
29997         /**
29998          * @event markerClick
29999          * Fires when click the marker.
30000          * @param {Roo.bootstrap.LocationPicker} this
30001          * @param {Map event} e
30002          */
30003         markerClick : true,
30004         /**
30005          * @event markerRightClick
30006          * Fires when right click the marker.
30007          * @param {Roo.bootstrap.LocationPicker} this
30008          * @param {Map event} e
30009          */
30010         markerRightClick : true,
30011         /**
30012          * @event OverlayViewDraw
30013          * Fires when OverlayView Draw
30014          * @param {Roo.bootstrap.LocationPicker} this
30015          */
30016         OverlayViewDraw : true,
30017         /**
30018          * @event OverlayViewOnAdd
30019          * Fires when OverlayView Draw
30020          * @param {Roo.bootstrap.LocationPicker} this
30021          */
30022         OverlayViewOnAdd : true,
30023         /**
30024          * @event OverlayViewOnRemove
30025          * Fires when OverlayView Draw
30026          * @param {Roo.bootstrap.LocationPicker} this
30027          */
30028         OverlayViewOnRemove : true,
30029         /**
30030          * @event OverlayViewShow
30031          * Fires when OverlayView Draw
30032          * @param {Roo.bootstrap.LocationPicker} this
30033          * @param {Pixel} cpx
30034          */
30035         OverlayViewShow : true,
30036         /**
30037          * @event OverlayViewHide
30038          * Fires when OverlayView Draw
30039          * @param {Roo.bootstrap.LocationPicker} this
30040          */
30041         OverlayViewHide : true,
30042         /**
30043          * @event loadexception
30044          * Fires when load google lib failed.
30045          * @param {Roo.bootstrap.LocationPicker} this
30046          */
30047         loadexception : true
30048     });
30049         
30050 };
30051
30052 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30053     
30054     gMapContext: false,
30055     
30056     latitude: 0,
30057     longitude: 0,
30058     zoom: 15,
30059     mapTypeId: false,
30060     mapTypeControl: false,
30061     disableDoubleClickZoom: false,
30062     scrollwheel: true,
30063     streetViewControl: false,
30064     radius: 0,
30065     locationName: '',
30066     draggable: true,
30067     enableAutocomplete: false,
30068     enableReverseGeocode: true,
30069     markerTitle: '',
30070     
30071     getAutoCreate: function()
30072     {
30073
30074         var cfg = {
30075             tag: 'div',
30076             cls: 'roo-location-picker'
30077         };
30078         
30079         return cfg
30080     },
30081     
30082     initEvents: function(ct, position)
30083     {       
30084         if(!this.el.getWidth() || this.isApplied()){
30085             return;
30086         }
30087         
30088         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30089         
30090         this.initial();
30091     },
30092     
30093     initial: function()
30094     {
30095         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30096             this.fireEvent('loadexception', this);
30097             return;
30098         }
30099         
30100         if(!this.mapTypeId){
30101             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30102         }
30103         
30104         this.gMapContext = this.GMapContext();
30105         
30106         this.initOverlayView();
30107         
30108         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30109         
30110         var _this = this;
30111                 
30112         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30113             _this.setPosition(_this.gMapContext.marker.position);
30114         });
30115         
30116         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30117             _this.fireEvent('mapClick', this, event);
30118             
30119         });
30120
30121         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30122             _this.fireEvent('mapRightClick', this, event);
30123             
30124         });
30125         
30126         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30127             _this.fireEvent('markerClick', this, event);
30128             
30129         });
30130
30131         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30132             _this.fireEvent('markerRightClick', this, event);
30133             
30134         });
30135         
30136         this.setPosition(this.gMapContext.location);
30137         
30138         this.fireEvent('initial', this, this.gMapContext.location);
30139     },
30140     
30141     initOverlayView: function()
30142     {
30143         var _this = this;
30144         
30145         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30146             
30147             draw: function()
30148             {
30149                 _this.fireEvent('OverlayViewDraw', _this);
30150             },
30151             
30152             onAdd: function()
30153             {
30154                 _this.fireEvent('OverlayViewOnAdd', _this);
30155             },
30156             
30157             onRemove: function()
30158             {
30159                 _this.fireEvent('OverlayViewOnRemove', _this);
30160             },
30161             
30162             show: function(cpx)
30163             {
30164                 _this.fireEvent('OverlayViewShow', _this, cpx);
30165             },
30166             
30167             hide: function()
30168             {
30169                 _this.fireEvent('OverlayViewHide', _this);
30170             }
30171             
30172         });
30173     },
30174     
30175     fromLatLngToContainerPixel: function(event)
30176     {
30177         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30178     },
30179     
30180     isApplied: function() 
30181     {
30182         return this.getGmapContext() == false ? false : true;
30183     },
30184     
30185     getGmapContext: function() 
30186     {
30187         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30188     },
30189     
30190     GMapContext: function() 
30191     {
30192         var position = new google.maps.LatLng(this.latitude, this.longitude);
30193         
30194         var _map = new google.maps.Map(this.el.dom, {
30195             center: position,
30196             zoom: this.zoom,
30197             mapTypeId: this.mapTypeId,
30198             mapTypeControl: this.mapTypeControl,
30199             disableDoubleClickZoom: this.disableDoubleClickZoom,
30200             scrollwheel: this.scrollwheel,
30201             streetViewControl: this.streetViewControl,
30202             locationName: this.locationName,
30203             draggable: this.draggable,
30204             enableAutocomplete: this.enableAutocomplete,
30205             enableReverseGeocode: this.enableReverseGeocode
30206         });
30207         
30208         var _marker = new google.maps.Marker({
30209             position: position,
30210             map: _map,
30211             title: this.markerTitle,
30212             draggable: this.draggable
30213         });
30214         
30215         return {
30216             map: _map,
30217             marker: _marker,
30218             circle: null,
30219             location: position,
30220             radius: this.radius,
30221             locationName: this.locationName,
30222             addressComponents: {
30223                 formatted_address: null,
30224                 addressLine1: null,
30225                 addressLine2: null,
30226                 streetName: null,
30227                 streetNumber: null,
30228                 city: null,
30229                 district: null,
30230                 state: null,
30231                 stateOrProvince: null
30232             },
30233             settings: this,
30234             domContainer: this.el.dom,
30235             geodecoder: new google.maps.Geocoder()
30236         };
30237     },
30238     
30239     drawCircle: function(center, radius, options) 
30240     {
30241         if (this.gMapContext.circle != null) {
30242             this.gMapContext.circle.setMap(null);
30243         }
30244         if (radius > 0) {
30245             radius *= 1;
30246             options = Roo.apply({}, options, {
30247                 strokeColor: "#0000FF",
30248                 strokeOpacity: .35,
30249                 strokeWeight: 2,
30250                 fillColor: "#0000FF",
30251                 fillOpacity: .2
30252             });
30253             
30254             options.map = this.gMapContext.map;
30255             options.radius = radius;
30256             options.center = center;
30257             this.gMapContext.circle = new google.maps.Circle(options);
30258             return this.gMapContext.circle;
30259         }
30260         
30261         return null;
30262     },
30263     
30264     setPosition: function(location) 
30265     {
30266         this.gMapContext.location = location;
30267         this.gMapContext.marker.setPosition(location);
30268         this.gMapContext.map.panTo(location);
30269         this.drawCircle(location, this.gMapContext.radius, {});
30270         
30271         var _this = this;
30272         
30273         if (this.gMapContext.settings.enableReverseGeocode) {
30274             this.gMapContext.geodecoder.geocode({
30275                 latLng: this.gMapContext.location
30276             }, function(results, status) {
30277                 
30278                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30279                     _this.gMapContext.locationName = results[0].formatted_address;
30280                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30281                     
30282                     _this.fireEvent('positionchanged', this, location);
30283                 }
30284             });
30285             
30286             return;
30287         }
30288         
30289         this.fireEvent('positionchanged', this, location);
30290     },
30291     
30292     resize: function()
30293     {
30294         google.maps.event.trigger(this.gMapContext.map, "resize");
30295         
30296         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30297         
30298         this.fireEvent('resize', this);
30299     },
30300     
30301     setPositionByLatLng: function(latitude, longitude)
30302     {
30303         this.setPosition(new google.maps.LatLng(latitude, longitude));
30304     },
30305     
30306     getCurrentPosition: function() 
30307     {
30308         return {
30309             latitude: this.gMapContext.location.lat(),
30310             longitude: this.gMapContext.location.lng()
30311         };
30312     },
30313     
30314     getAddressName: function() 
30315     {
30316         return this.gMapContext.locationName;
30317     },
30318     
30319     getAddressComponents: function() 
30320     {
30321         return this.gMapContext.addressComponents;
30322     },
30323     
30324     address_component_from_google_geocode: function(address_components) 
30325     {
30326         var result = {};
30327         
30328         for (var i = 0; i < address_components.length; i++) {
30329             var component = address_components[i];
30330             if (component.types.indexOf("postal_code") >= 0) {
30331                 result.postalCode = component.short_name;
30332             } else if (component.types.indexOf("street_number") >= 0) {
30333                 result.streetNumber = component.short_name;
30334             } else if (component.types.indexOf("route") >= 0) {
30335                 result.streetName = component.short_name;
30336             } else if (component.types.indexOf("neighborhood") >= 0) {
30337                 result.city = component.short_name;
30338             } else if (component.types.indexOf("locality") >= 0) {
30339                 result.city = component.short_name;
30340             } else if (component.types.indexOf("sublocality") >= 0) {
30341                 result.district = component.short_name;
30342             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30343                 result.stateOrProvince = component.short_name;
30344             } else if (component.types.indexOf("country") >= 0) {
30345                 result.country = component.short_name;
30346             }
30347         }
30348         
30349         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30350         result.addressLine2 = "";
30351         return result;
30352     },
30353     
30354     setZoomLevel: function(zoom)
30355     {
30356         this.gMapContext.map.setZoom(zoom);
30357     },
30358     
30359     show: function()
30360     {
30361         if(!this.el){
30362             return;
30363         }
30364         
30365         this.el.show();
30366         
30367         this.resize();
30368         
30369         this.fireEvent('show', this);
30370     },
30371     
30372     hide: function()
30373     {
30374         if(!this.el){
30375             return;
30376         }
30377         
30378         this.el.hide();
30379         
30380         this.fireEvent('hide', this);
30381     }
30382     
30383 });
30384
30385 Roo.apply(Roo.bootstrap.LocationPicker, {
30386     
30387     OverlayView : function(map, options)
30388     {
30389         options = options || {};
30390         
30391         this.setMap(map);
30392     }
30393     
30394     
30395 });/**
30396  * @class Roo.bootstrap.Alert
30397  * @extends Roo.bootstrap.Component
30398  * Bootstrap Alert class - shows an alert area box
30399  * eg
30400  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30401   Enter a valid email address
30402 </div>
30403  * @licence LGPL
30404  * @cfg {String} title The title of alert
30405  * @cfg {String} html The content of alert
30406  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30407  * @cfg {String} fa font-awesomeicon
30408  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30409  * @cfg {Boolean} close true to show a x closer
30410  * 
30411  * 
30412  * @constructor
30413  * Create a new alert
30414  * @param {Object} config The config object
30415  */
30416
30417
30418 Roo.bootstrap.Alert = function(config){
30419     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30420     
30421 };
30422
30423 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30424     
30425     title: '',
30426     html: '',
30427     weight: false,
30428     fa: false,
30429     faicon: false, // BC
30430     close : false,
30431     
30432     
30433     getAutoCreate : function()
30434     {
30435         
30436         var cfg = {
30437             tag : 'div',
30438             cls : 'alert',
30439             cn : [
30440                 {
30441                     tag: 'button',
30442                     type :  "button",
30443                     cls: "close",
30444                     html : '×',
30445                     style : this.close ? '' : 'display:none'
30446                 },
30447                 {
30448                     tag : 'i',
30449                     cls : 'roo-alert-icon'
30450                     
30451                 },
30452                 {
30453                     tag : 'b',
30454                     cls : 'roo-alert-title',
30455                     html : this.title
30456                 },
30457                 {
30458                     tag : 'span',
30459                     cls : 'roo-alert-text',
30460                     html : this.html
30461                 }
30462             ]
30463         };
30464         
30465         if(this.faicon){
30466             cfg.cn[0].cls += ' fa ' + this.faicon;
30467         }
30468         if(this.fa){
30469             cfg.cn[0].cls += ' fa ' + this.fa;
30470         }
30471         
30472         if(this.weight){
30473             cfg.cls += ' alert-' + this.weight;
30474         }
30475         
30476         return cfg;
30477     },
30478     
30479     initEvents: function() 
30480     {
30481         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30482         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30483         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30484         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30485         if (this.seconds > 0) {
30486             this.hide.defer(this.seconds, this);
30487         }
30488     },
30489     /**
30490      * Set the Title Message HTML
30491      * @param {String} html
30492      */
30493     setTitle : function(str)
30494     {
30495         this.titleEl.dom.innerHTML = str;
30496     },
30497      
30498      /**
30499      * Set the Body Message HTML
30500      * @param {String} html
30501      */
30502     setHtml : function(str)
30503     {
30504         this.htmlEl.dom.innerHTML = str;
30505     },
30506     /**
30507      * Set the Weight of the alert
30508      * @param {String} (success|info|warning|danger) weight
30509      */
30510     
30511     setWeight : function(weight)
30512     {
30513         if(this.weight){
30514             this.el.removeClass('alert-' + this.weight);
30515         }
30516         
30517         this.weight = weight;
30518         
30519         this.el.addClass('alert-' + this.weight);
30520     },
30521       /**
30522      * Set the Icon of the alert
30523      * @param {String} see fontawsome names (name without the 'fa-' bit)
30524      */
30525     setIcon : function(icon)
30526     {
30527         if(this.faicon){
30528             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30529         }
30530         
30531         this.faicon = icon;
30532         
30533         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30534     },
30535     /**
30536      * Hide the Alert
30537      */
30538     hide: function() 
30539     {
30540         this.el.hide();   
30541     },
30542     /**
30543      * Show the Alert
30544      */
30545     show: function() 
30546     {  
30547         this.el.show();   
30548     }
30549     
30550 });
30551
30552  
30553 /*
30554 * Licence: LGPL
30555 */
30556
30557 /**
30558  * @class Roo.bootstrap.UploadCropbox
30559  * @extends Roo.bootstrap.Component
30560  * Bootstrap UploadCropbox class
30561  * @cfg {String} emptyText show when image has been loaded
30562  * @cfg {String} rotateNotify show when image too small to rotate
30563  * @cfg {Number} errorTimeout default 3000
30564  * @cfg {Number} minWidth default 300
30565  * @cfg {Number} minHeight default 300
30566  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30567  * @cfg {Boolean} isDocument (true|false) default false
30568  * @cfg {String} url action url
30569  * @cfg {String} paramName default 'imageUpload'
30570  * @cfg {String} method default POST
30571  * @cfg {Boolean} loadMask (true|false) default true
30572  * @cfg {Boolean} loadingText default 'Loading...'
30573  * 
30574  * @constructor
30575  * Create a new UploadCropbox
30576  * @param {Object} config The config object
30577  */
30578
30579 Roo.bootstrap.UploadCropbox = function(config){
30580     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30581     
30582     this.addEvents({
30583         /**
30584          * @event beforeselectfile
30585          * Fire before select file
30586          * @param {Roo.bootstrap.UploadCropbox} this
30587          */
30588         "beforeselectfile" : true,
30589         /**
30590          * @event initial
30591          * Fire after initEvent
30592          * @param {Roo.bootstrap.UploadCropbox} this
30593          */
30594         "initial" : true,
30595         /**
30596          * @event crop
30597          * Fire after initEvent
30598          * @param {Roo.bootstrap.UploadCropbox} this
30599          * @param {String} data
30600          */
30601         "crop" : true,
30602         /**
30603          * @event prepare
30604          * Fire when preparing the file data
30605          * @param {Roo.bootstrap.UploadCropbox} this
30606          * @param {Object} file
30607          */
30608         "prepare" : true,
30609         /**
30610          * @event exception
30611          * Fire when get exception
30612          * @param {Roo.bootstrap.UploadCropbox} this
30613          * @param {XMLHttpRequest} xhr
30614          */
30615         "exception" : true,
30616         /**
30617          * @event beforeloadcanvas
30618          * Fire before load the canvas
30619          * @param {Roo.bootstrap.UploadCropbox} this
30620          * @param {String} src
30621          */
30622         "beforeloadcanvas" : true,
30623         /**
30624          * @event trash
30625          * Fire when trash image
30626          * @param {Roo.bootstrap.UploadCropbox} this
30627          */
30628         "trash" : true,
30629         /**
30630          * @event download
30631          * Fire when download the image
30632          * @param {Roo.bootstrap.UploadCropbox} this
30633          */
30634         "download" : true,
30635         /**
30636          * @event footerbuttonclick
30637          * Fire when footerbuttonclick
30638          * @param {Roo.bootstrap.UploadCropbox} this
30639          * @param {String} type
30640          */
30641         "footerbuttonclick" : true,
30642         /**
30643          * @event resize
30644          * Fire when resize
30645          * @param {Roo.bootstrap.UploadCropbox} this
30646          */
30647         "resize" : true,
30648         /**
30649          * @event rotate
30650          * Fire when rotate the image
30651          * @param {Roo.bootstrap.UploadCropbox} this
30652          * @param {String} pos
30653          */
30654         "rotate" : true,
30655         /**
30656          * @event inspect
30657          * Fire when inspect the file
30658          * @param {Roo.bootstrap.UploadCropbox} this
30659          * @param {Object} file
30660          */
30661         "inspect" : true,
30662         /**
30663          * @event upload
30664          * Fire when xhr upload the file
30665          * @param {Roo.bootstrap.UploadCropbox} this
30666          * @param {Object} data
30667          */
30668         "upload" : true,
30669         /**
30670          * @event arrange
30671          * Fire when arrange the file data
30672          * @param {Roo.bootstrap.UploadCropbox} this
30673          * @param {Object} formData
30674          */
30675         "arrange" : true
30676     });
30677     
30678     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30679 };
30680
30681 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30682     
30683     emptyText : 'Click to upload image',
30684     rotateNotify : 'Image is too small to rotate',
30685     errorTimeout : 3000,
30686     scale : 0,
30687     baseScale : 1,
30688     rotate : 0,
30689     dragable : false,
30690     pinching : false,
30691     mouseX : 0,
30692     mouseY : 0,
30693     cropData : false,
30694     minWidth : 300,
30695     minHeight : 300,
30696     file : false,
30697     exif : {},
30698     baseRotate : 1,
30699     cropType : 'image/jpeg',
30700     buttons : false,
30701     canvasLoaded : false,
30702     isDocument : false,
30703     method : 'POST',
30704     paramName : 'imageUpload',
30705     loadMask : true,
30706     loadingText : 'Loading...',
30707     maskEl : false,
30708     
30709     getAutoCreate : function()
30710     {
30711         var cfg = {
30712             tag : 'div',
30713             cls : 'roo-upload-cropbox',
30714             cn : [
30715                 {
30716                     tag : 'input',
30717                     cls : 'roo-upload-cropbox-selector',
30718                     type : 'file'
30719                 },
30720                 {
30721                     tag : 'div',
30722                     cls : 'roo-upload-cropbox-body',
30723                     style : 'cursor:pointer',
30724                     cn : [
30725                         {
30726                             tag : 'div',
30727                             cls : 'roo-upload-cropbox-preview'
30728                         },
30729                         {
30730                             tag : 'div',
30731                             cls : 'roo-upload-cropbox-thumb'
30732                         },
30733                         {
30734                             tag : 'div',
30735                             cls : 'roo-upload-cropbox-empty-notify',
30736                             html : this.emptyText
30737                         },
30738                         {
30739                             tag : 'div',
30740                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30741                             html : this.rotateNotify
30742                         }
30743                     ]
30744                 },
30745                 {
30746                     tag : 'div',
30747                     cls : 'roo-upload-cropbox-footer',
30748                     cn : {
30749                         tag : 'div',
30750                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30751                         cn : []
30752                     }
30753                 }
30754             ]
30755         };
30756         
30757         return cfg;
30758     },
30759     
30760     onRender : function(ct, position)
30761     {
30762         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30763         
30764         if (this.buttons.length) {
30765             
30766             Roo.each(this.buttons, function(bb) {
30767                 
30768                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30769                 
30770                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30771                 
30772             }, this);
30773         }
30774         
30775         if(this.loadMask){
30776             this.maskEl = this.el;
30777         }
30778     },
30779     
30780     initEvents : function()
30781     {
30782         this.urlAPI = (window.createObjectURL && window) || 
30783                                 (window.URL && URL.revokeObjectURL && URL) || 
30784                                 (window.webkitURL && webkitURL);
30785                         
30786         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30787         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30788         
30789         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30790         this.selectorEl.hide();
30791         
30792         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30793         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30794         
30795         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30796         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30797         this.thumbEl.hide();
30798         
30799         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30800         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30801         
30802         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30803         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30804         this.errorEl.hide();
30805         
30806         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30807         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808         this.footerEl.hide();
30809         
30810         this.setThumbBoxSize();
30811         
30812         this.bind();
30813         
30814         this.resize();
30815         
30816         this.fireEvent('initial', this);
30817     },
30818
30819     bind : function()
30820     {
30821         var _this = this;
30822         
30823         window.addEventListener("resize", function() { _this.resize(); } );
30824         
30825         this.bodyEl.on('click', this.beforeSelectFile, this);
30826         
30827         if(Roo.isTouch){
30828             this.bodyEl.on('touchstart', this.onTouchStart, this);
30829             this.bodyEl.on('touchmove', this.onTouchMove, this);
30830             this.bodyEl.on('touchend', this.onTouchEnd, this);
30831         }
30832         
30833         if(!Roo.isTouch){
30834             this.bodyEl.on('mousedown', this.onMouseDown, this);
30835             this.bodyEl.on('mousemove', this.onMouseMove, this);
30836             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30837             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30838             Roo.get(document).on('mouseup', this.onMouseUp, this);
30839         }
30840         
30841         this.selectorEl.on('change', this.onFileSelected, this);
30842     },
30843     
30844     reset : function()
30845     {    
30846         this.scale = 0;
30847         this.baseScale = 1;
30848         this.rotate = 0;
30849         this.baseRotate = 1;
30850         this.dragable = false;
30851         this.pinching = false;
30852         this.mouseX = 0;
30853         this.mouseY = 0;
30854         this.cropData = false;
30855         this.notifyEl.dom.innerHTML = this.emptyText;
30856         
30857         this.selectorEl.dom.value = '';
30858         
30859     },
30860     
30861     resize : function()
30862     {
30863         if(this.fireEvent('resize', this) != false){
30864             this.setThumbBoxPosition();
30865             this.setCanvasPosition();
30866         }
30867     },
30868     
30869     onFooterButtonClick : function(e, el, o, type)
30870     {
30871         switch (type) {
30872             case 'rotate-left' :
30873                 this.onRotateLeft(e);
30874                 break;
30875             case 'rotate-right' :
30876                 this.onRotateRight(e);
30877                 break;
30878             case 'picture' :
30879                 this.beforeSelectFile(e);
30880                 break;
30881             case 'trash' :
30882                 this.trash(e);
30883                 break;
30884             case 'crop' :
30885                 this.crop(e);
30886                 break;
30887             case 'download' :
30888                 this.download(e);
30889                 break;
30890             default :
30891                 break;
30892         }
30893         
30894         this.fireEvent('footerbuttonclick', this, type);
30895     },
30896     
30897     beforeSelectFile : function(e)
30898     {
30899         e.preventDefault();
30900         
30901         if(this.fireEvent('beforeselectfile', this) != false){
30902             this.selectorEl.dom.click();
30903         }
30904     },
30905     
30906     onFileSelected : function(e)
30907     {
30908         e.preventDefault();
30909         
30910         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30911             return;
30912         }
30913         
30914         var file = this.selectorEl.dom.files[0];
30915         
30916         if(this.fireEvent('inspect', this, file) != false){
30917             this.prepare(file);
30918         }
30919         
30920     },
30921     
30922     trash : function(e)
30923     {
30924         this.fireEvent('trash', this);
30925     },
30926     
30927     download : function(e)
30928     {
30929         this.fireEvent('download', this);
30930     },
30931     
30932     loadCanvas : function(src)
30933     {   
30934         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30935             
30936             this.reset();
30937             
30938             this.imageEl = document.createElement('img');
30939             
30940             var _this = this;
30941             
30942             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30943             
30944             this.imageEl.src = src;
30945         }
30946     },
30947     
30948     onLoadCanvas : function()
30949     {   
30950         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30951         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30952         
30953         this.bodyEl.un('click', this.beforeSelectFile, this);
30954         
30955         this.notifyEl.hide();
30956         this.thumbEl.show();
30957         this.footerEl.show();
30958         
30959         this.baseRotateLevel();
30960         
30961         if(this.isDocument){
30962             this.setThumbBoxSize();
30963         }
30964         
30965         this.setThumbBoxPosition();
30966         
30967         this.baseScaleLevel();
30968         
30969         this.draw();
30970         
30971         this.resize();
30972         
30973         this.canvasLoaded = true;
30974         
30975         if(this.loadMask){
30976             this.maskEl.unmask();
30977         }
30978         
30979     },
30980     
30981     setCanvasPosition : function()
30982     {   
30983         if(!this.canvasEl){
30984             return;
30985         }
30986         
30987         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30988         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30989         
30990         this.previewEl.setLeft(pw);
30991         this.previewEl.setTop(ph);
30992         
30993     },
30994     
30995     onMouseDown : function(e)
30996     {   
30997         e.stopEvent();
30998         
30999         this.dragable = true;
31000         this.pinching = false;
31001         
31002         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31003             this.dragable = false;
31004             return;
31005         }
31006         
31007         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31008         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31009         
31010     },
31011     
31012     onMouseMove : function(e)
31013     {   
31014         e.stopEvent();
31015         
31016         if(!this.canvasLoaded){
31017             return;
31018         }
31019         
31020         if (!this.dragable){
31021             return;
31022         }
31023         
31024         var minX = Math.ceil(this.thumbEl.getLeft(true));
31025         var minY = Math.ceil(this.thumbEl.getTop(true));
31026         
31027         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31028         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31029         
31030         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31031         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31032         
31033         x = x - this.mouseX;
31034         y = y - this.mouseY;
31035         
31036         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31037         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31038         
31039         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31040         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31041         
31042         this.previewEl.setLeft(bgX);
31043         this.previewEl.setTop(bgY);
31044         
31045         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31046         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31047     },
31048     
31049     onMouseUp : function(e)
31050     {   
31051         e.stopEvent();
31052         
31053         this.dragable = false;
31054     },
31055     
31056     onMouseWheel : function(e)
31057     {   
31058         e.stopEvent();
31059         
31060         this.startScale = this.scale;
31061         
31062         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31063         
31064         if(!this.zoomable()){
31065             this.scale = this.startScale;
31066             return;
31067         }
31068         
31069         this.draw();
31070         
31071         return;
31072     },
31073     
31074     zoomable : function()
31075     {
31076         var minScale = this.thumbEl.getWidth() / this.minWidth;
31077         
31078         if(this.minWidth < this.minHeight){
31079             minScale = this.thumbEl.getHeight() / this.minHeight;
31080         }
31081         
31082         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31083         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31084         
31085         if(
31086                 this.isDocument &&
31087                 (this.rotate == 0 || this.rotate == 180) && 
31088                 (
31089                     width > this.imageEl.OriginWidth || 
31090                     height > this.imageEl.OriginHeight ||
31091                     (width < this.minWidth && height < this.minHeight)
31092                 )
31093         ){
31094             return false;
31095         }
31096         
31097         if(
31098                 this.isDocument &&
31099                 (this.rotate == 90 || this.rotate == 270) && 
31100                 (
31101                     width > this.imageEl.OriginWidth || 
31102                     height > this.imageEl.OriginHeight ||
31103                     (width < this.minHeight && height < this.minWidth)
31104                 )
31105         ){
31106             return false;
31107         }
31108         
31109         if(
31110                 !this.isDocument &&
31111                 (this.rotate == 0 || this.rotate == 180) && 
31112                 (
31113                     width < this.minWidth || 
31114                     width > this.imageEl.OriginWidth || 
31115                     height < this.minHeight || 
31116                     height > this.imageEl.OriginHeight
31117                 )
31118         ){
31119             return false;
31120         }
31121         
31122         if(
31123                 !this.isDocument &&
31124                 (this.rotate == 90 || this.rotate == 270) && 
31125                 (
31126                     width < this.minHeight || 
31127                     width > this.imageEl.OriginWidth || 
31128                     height < this.minWidth || 
31129                     height > this.imageEl.OriginHeight
31130                 )
31131         ){
31132             return false;
31133         }
31134         
31135         return true;
31136         
31137     },
31138     
31139     onRotateLeft : function(e)
31140     {   
31141         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31142             
31143             var minScale = this.thumbEl.getWidth() / this.minWidth;
31144             
31145             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31146             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31147             
31148             this.startScale = this.scale;
31149             
31150             while (this.getScaleLevel() < minScale){
31151             
31152                 this.scale = this.scale + 1;
31153                 
31154                 if(!this.zoomable()){
31155                     break;
31156                 }
31157                 
31158                 if(
31159                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31160                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31161                 ){
31162                     continue;
31163                 }
31164                 
31165                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31166
31167                 this.draw();
31168                 
31169                 return;
31170             }
31171             
31172             this.scale = this.startScale;
31173             
31174             this.onRotateFail();
31175             
31176             return false;
31177         }
31178         
31179         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31180
31181         if(this.isDocument){
31182             this.setThumbBoxSize();
31183             this.setThumbBoxPosition();
31184             this.setCanvasPosition();
31185         }
31186         
31187         this.draw();
31188         
31189         this.fireEvent('rotate', this, 'left');
31190         
31191     },
31192     
31193     onRotateRight : function(e)
31194     {
31195         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31196             
31197             var minScale = this.thumbEl.getWidth() / this.minWidth;
31198         
31199             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31200             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31201             
31202             this.startScale = this.scale;
31203             
31204             while (this.getScaleLevel() < minScale){
31205             
31206                 this.scale = this.scale + 1;
31207                 
31208                 if(!this.zoomable()){
31209                     break;
31210                 }
31211                 
31212                 if(
31213                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31214                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31215                 ){
31216                     continue;
31217                 }
31218                 
31219                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31220
31221                 this.draw();
31222                 
31223                 return;
31224             }
31225             
31226             this.scale = this.startScale;
31227             
31228             this.onRotateFail();
31229             
31230             return false;
31231         }
31232         
31233         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31234
31235         if(this.isDocument){
31236             this.setThumbBoxSize();
31237             this.setThumbBoxPosition();
31238             this.setCanvasPosition();
31239         }
31240         
31241         this.draw();
31242         
31243         this.fireEvent('rotate', this, 'right');
31244     },
31245     
31246     onRotateFail : function()
31247     {
31248         this.errorEl.show(true);
31249         
31250         var _this = this;
31251         
31252         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31253     },
31254     
31255     draw : function()
31256     {
31257         this.previewEl.dom.innerHTML = '';
31258         
31259         var canvasEl = document.createElement("canvas");
31260         
31261         var contextEl = canvasEl.getContext("2d");
31262         
31263         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31264         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31265         var center = this.imageEl.OriginWidth / 2;
31266         
31267         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31268             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31269             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31270             center = this.imageEl.OriginHeight / 2;
31271         }
31272         
31273         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31274         
31275         contextEl.translate(center, center);
31276         contextEl.rotate(this.rotate * Math.PI / 180);
31277
31278         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31279         
31280         this.canvasEl = document.createElement("canvas");
31281         
31282         this.contextEl = this.canvasEl.getContext("2d");
31283         
31284         switch (this.rotate) {
31285             case 0 :
31286                 
31287                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31288                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31289                 
31290                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31291                 
31292                 break;
31293             case 90 : 
31294                 
31295                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31296                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31297                 
31298                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31299                     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);
31300                     break;
31301                 }
31302                 
31303                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31304                 
31305                 break;
31306             case 180 :
31307                 
31308                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31309                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31310                 
31311                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31312                     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);
31313                     break;
31314                 }
31315                 
31316                 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);
31317                 
31318                 break;
31319             case 270 :
31320                 
31321                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31322                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31323         
31324                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31325                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31326                     break;
31327                 }
31328                 
31329                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31330                 
31331                 break;
31332             default : 
31333                 break;
31334         }
31335         
31336         this.previewEl.appendChild(this.canvasEl);
31337         
31338         this.setCanvasPosition();
31339     },
31340     
31341     crop : function()
31342     {
31343         if(!this.canvasLoaded){
31344             return;
31345         }
31346         
31347         var imageCanvas = document.createElement("canvas");
31348         
31349         var imageContext = imageCanvas.getContext("2d");
31350         
31351         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31352         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31353         
31354         var center = imageCanvas.width / 2;
31355         
31356         imageContext.translate(center, center);
31357         
31358         imageContext.rotate(this.rotate * Math.PI / 180);
31359         
31360         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31361         
31362         var canvas = document.createElement("canvas");
31363         
31364         var context = canvas.getContext("2d");
31365                 
31366         canvas.width = this.minWidth;
31367         canvas.height = this.minHeight;
31368
31369         switch (this.rotate) {
31370             case 0 :
31371                 
31372                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31373                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31374                 
31375                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31376                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31377                 
31378                 var targetWidth = this.minWidth - 2 * x;
31379                 var targetHeight = this.minHeight - 2 * y;
31380                 
31381                 var scale = 1;
31382                 
31383                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31384                     scale = targetWidth / width;
31385                 }
31386                 
31387                 if(x > 0 && y == 0){
31388                     scale = targetHeight / height;
31389                 }
31390                 
31391                 if(x > 0 && y > 0){
31392                     scale = targetWidth / width;
31393                     
31394                     if(width < height){
31395                         scale = targetHeight / height;
31396                     }
31397                 }
31398                 
31399                 context.scale(scale, scale);
31400                 
31401                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31402                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31403
31404                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31405                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31406
31407                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31408                 
31409                 break;
31410             case 90 : 
31411                 
31412                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31413                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31414                 
31415                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31416                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31417                 
31418                 var targetWidth = this.minWidth - 2 * x;
31419                 var targetHeight = this.minHeight - 2 * y;
31420                 
31421                 var scale = 1;
31422                 
31423                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31424                     scale = targetWidth / width;
31425                 }
31426                 
31427                 if(x > 0 && y == 0){
31428                     scale = targetHeight / height;
31429                 }
31430                 
31431                 if(x > 0 && y > 0){
31432                     scale = targetWidth / width;
31433                     
31434                     if(width < height){
31435                         scale = targetHeight / height;
31436                     }
31437                 }
31438                 
31439                 context.scale(scale, scale);
31440                 
31441                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31442                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31443
31444                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31445                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31446                 
31447                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31448                 
31449                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31450                 
31451                 break;
31452             case 180 :
31453                 
31454                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31455                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31456                 
31457                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31458                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31459                 
31460                 var targetWidth = this.minWidth - 2 * x;
31461                 var targetHeight = this.minHeight - 2 * y;
31462                 
31463                 var scale = 1;
31464                 
31465                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31466                     scale = targetWidth / width;
31467                 }
31468                 
31469                 if(x > 0 && y == 0){
31470                     scale = targetHeight / height;
31471                 }
31472                 
31473                 if(x > 0 && y > 0){
31474                     scale = targetWidth / width;
31475                     
31476                     if(width < height){
31477                         scale = targetHeight / height;
31478                     }
31479                 }
31480                 
31481                 context.scale(scale, scale);
31482                 
31483                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31484                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31485
31486                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31487                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31488
31489                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31490                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31491                 
31492                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31493                 
31494                 break;
31495             case 270 :
31496                 
31497                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31498                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31499                 
31500                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31501                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31502                 
31503                 var targetWidth = this.minWidth - 2 * x;
31504                 var targetHeight = this.minHeight - 2 * y;
31505                 
31506                 var scale = 1;
31507                 
31508                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31509                     scale = targetWidth / width;
31510                 }
31511                 
31512                 if(x > 0 && y == 0){
31513                     scale = targetHeight / height;
31514                 }
31515                 
31516                 if(x > 0 && y > 0){
31517                     scale = targetWidth / width;
31518                     
31519                     if(width < height){
31520                         scale = targetHeight / height;
31521                     }
31522                 }
31523                 
31524                 context.scale(scale, scale);
31525                 
31526                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31527                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31528
31529                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31530                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31531                 
31532                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31533                 
31534                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31535                 
31536                 break;
31537             default : 
31538                 break;
31539         }
31540         
31541         this.cropData = canvas.toDataURL(this.cropType);
31542         
31543         if(this.fireEvent('crop', this, this.cropData) !== false){
31544             this.process(this.file, this.cropData);
31545         }
31546         
31547         return;
31548         
31549     },
31550     
31551     setThumbBoxSize : function()
31552     {
31553         var width, height;
31554         
31555         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31556             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31557             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31558             
31559             this.minWidth = width;
31560             this.minHeight = height;
31561             
31562             if(this.rotate == 90 || this.rotate == 270){
31563                 this.minWidth = height;
31564                 this.minHeight = width;
31565             }
31566         }
31567         
31568         height = 300;
31569         width = Math.ceil(this.minWidth * height / this.minHeight);
31570         
31571         if(this.minWidth > this.minHeight){
31572             width = 300;
31573             height = Math.ceil(this.minHeight * width / this.minWidth);
31574         }
31575         
31576         this.thumbEl.setStyle({
31577             width : width + 'px',
31578             height : height + 'px'
31579         });
31580
31581         return;
31582             
31583     },
31584     
31585     setThumbBoxPosition : function()
31586     {
31587         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31588         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31589         
31590         this.thumbEl.setLeft(x);
31591         this.thumbEl.setTop(y);
31592         
31593     },
31594     
31595     baseRotateLevel : function()
31596     {
31597         this.baseRotate = 1;
31598         
31599         if(
31600                 typeof(this.exif) != 'undefined' &&
31601                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31602                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31603         ){
31604             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31605         }
31606         
31607         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31608         
31609     },
31610     
31611     baseScaleLevel : function()
31612     {
31613         var width, height;
31614         
31615         if(this.isDocument){
31616             
31617             if(this.baseRotate == 6 || this.baseRotate == 8){
31618             
31619                 height = this.thumbEl.getHeight();
31620                 this.baseScale = height / this.imageEl.OriginWidth;
31621
31622                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31623                     width = this.thumbEl.getWidth();
31624                     this.baseScale = width / this.imageEl.OriginHeight;
31625                 }
31626
31627                 return;
31628             }
31629
31630             height = this.thumbEl.getHeight();
31631             this.baseScale = height / this.imageEl.OriginHeight;
31632
31633             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31634                 width = this.thumbEl.getWidth();
31635                 this.baseScale = width / this.imageEl.OriginWidth;
31636             }
31637
31638             return;
31639         }
31640         
31641         if(this.baseRotate == 6 || this.baseRotate == 8){
31642             
31643             width = this.thumbEl.getHeight();
31644             this.baseScale = width / this.imageEl.OriginHeight;
31645             
31646             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31647                 height = this.thumbEl.getWidth();
31648                 this.baseScale = height / this.imageEl.OriginHeight;
31649             }
31650             
31651             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31652                 height = this.thumbEl.getWidth();
31653                 this.baseScale = height / this.imageEl.OriginHeight;
31654                 
31655                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31656                     width = this.thumbEl.getHeight();
31657                     this.baseScale = width / this.imageEl.OriginWidth;
31658                 }
31659             }
31660             
31661             return;
31662         }
31663         
31664         width = this.thumbEl.getWidth();
31665         this.baseScale = width / this.imageEl.OriginWidth;
31666         
31667         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31668             height = this.thumbEl.getHeight();
31669             this.baseScale = height / this.imageEl.OriginHeight;
31670         }
31671         
31672         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31673             
31674             height = this.thumbEl.getHeight();
31675             this.baseScale = height / this.imageEl.OriginHeight;
31676             
31677             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31678                 width = this.thumbEl.getWidth();
31679                 this.baseScale = width / this.imageEl.OriginWidth;
31680             }
31681             
31682         }
31683         
31684         return;
31685     },
31686     
31687     getScaleLevel : function()
31688     {
31689         return this.baseScale * Math.pow(1.1, this.scale);
31690     },
31691     
31692     onTouchStart : function(e)
31693     {
31694         if(!this.canvasLoaded){
31695             this.beforeSelectFile(e);
31696             return;
31697         }
31698         
31699         var touches = e.browserEvent.touches;
31700         
31701         if(!touches){
31702             return;
31703         }
31704         
31705         if(touches.length == 1){
31706             this.onMouseDown(e);
31707             return;
31708         }
31709         
31710         if(touches.length != 2){
31711             return;
31712         }
31713         
31714         var coords = [];
31715         
31716         for(var i = 0, finger; finger = touches[i]; i++){
31717             coords.push(finger.pageX, finger.pageY);
31718         }
31719         
31720         var x = Math.pow(coords[0] - coords[2], 2);
31721         var y = Math.pow(coords[1] - coords[3], 2);
31722         
31723         this.startDistance = Math.sqrt(x + y);
31724         
31725         this.startScale = this.scale;
31726         
31727         this.pinching = true;
31728         this.dragable = false;
31729         
31730     },
31731     
31732     onTouchMove : function(e)
31733     {
31734         if(!this.pinching && !this.dragable){
31735             return;
31736         }
31737         
31738         var touches = e.browserEvent.touches;
31739         
31740         if(!touches){
31741             return;
31742         }
31743         
31744         if(this.dragable){
31745             this.onMouseMove(e);
31746             return;
31747         }
31748         
31749         var coords = [];
31750         
31751         for(var i = 0, finger; finger = touches[i]; i++){
31752             coords.push(finger.pageX, finger.pageY);
31753         }
31754         
31755         var x = Math.pow(coords[0] - coords[2], 2);
31756         var y = Math.pow(coords[1] - coords[3], 2);
31757         
31758         this.endDistance = Math.sqrt(x + y);
31759         
31760         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31761         
31762         if(!this.zoomable()){
31763             this.scale = this.startScale;
31764             return;
31765         }
31766         
31767         this.draw();
31768         
31769     },
31770     
31771     onTouchEnd : function(e)
31772     {
31773         this.pinching = false;
31774         this.dragable = false;
31775         
31776     },
31777     
31778     process : function(file, crop)
31779     {
31780         if(this.loadMask){
31781             this.maskEl.mask(this.loadingText);
31782         }
31783         
31784         this.xhr = new XMLHttpRequest();
31785         
31786         file.xhr = this.xhr;
31787
31788         this.xhr.open(this.method, this.url, true);
31789         
31790         var headers = {
31791             "Accept": "application/json",
31792             "Cache-Control": "no-cache",
31793             "X-Requested-With": "XMLHttpRequest"
31794         };
31795         
31796         for (var headerName in headers) {
31797             var headerValue = headers[headerName];
31798             if (headerValue) {
31799                 this.xhr.setRequestHeader(headerName, headerValue);
31800             }
31801         }
31802         
31803         var _this = this;
31804         
31805         this.xhr.onload = function()
31806         {
31807             _this.xhrOnLoad(_this.xhr);
31808         }
31809         
31810         this.xhr.onerror = function()
31811         {
31812             _this.xhrOnError(_this.xhr);
31813         }
31814         
31815         var formData = new FormData();
31816
31817         formData.append('returnHTML', 'NO');
31818         
31819         if(crop){
31820             formData.append('crop', crop);
31821         }
31822         
31823         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31824             formData.append(this.paramName, file, file.name);
31825         }
31826         
31827         if(typeof(file.filename) != 'undefined'){
31828             formData.append('filename', file.filename);
31829         }
31830         
31831         if(typeof(file.mimetype) != 'undefined'){
31832             formData.append('mimetype', file.mimetype);
31833         }
31834         
31835         if(this.fireEvent('arrange', this, formData) != false){
31836             this.xhr.send(formData);
31837         };
31838     },
31839     
31840     xhrOnLoad : function(xhr)
31841     {
31842         if(this.loadMask){
31843             this.maskEl.unmask();
31844         }
31845         
31846         if (xhr.readyState !== 4) {
31847             this.fireEvent('exception', this, xhr);
31848             return;
31849         }
31850
31851         var response = Roo.decode(xhr.responseText);
31852         
31853         if(!response.success){
31854             this.fireEvent('exception', this, xhr);
31855             return;
31856         }
31857         
31858         var response = Roo.decode(xhr.responseText);
31859         
31860         this.fireEvent('upload', this, response);
31861         
31862     },
31863     
31864     xhrOnError : function()
31865     {
31866         if(this.loadMask){
31867             this.maskEl.unmask();
31868         }
31869         
31870         Roo.log('xhr on error');
31871         
31872         var response = Roo.decode(xhr.responseText);
31873           
31874         Roo.log(response);
31875         
31876     },
31877     
31878     prepare : function(file)
31879     {   
31880         if(this.loadMask){
31881             this.maskEl.mask(this.loadingText);
31882         }
31883         
31884         this.file = false;
31885         this.exif = {};
31886         
31887         if(typeof(file) === 'string'){
31888             this.loadCanvas(file);
31889             return;
31890         }
31891         
31892         if(!file || !this.urlAPI){
31893             return;
31894         }
31895         
31896         this.file = file;
31897         this.cropType = file.type;
31898         
31899         var _this = this;
31900         
31901         if(this.fireEvent('prepare', this, this.file) != false){
31902             
31903             var reader = new FileReader();
31904             
31905             reader.onload = function (e) {
31906                 if (e.target.error) {
31907                     Roo.log(e.target.error);
31908                     return;
31909                 }
31910                 
31911                 var buffer = e.target.result,
31912                     dataView = new DataView(buffer),
31913                     offset = 2,
31914                     maxOffset = dataView.byteLength - 4,
31915                     markerBytes,
31916                     markerLength;
31917                 
31918                 if (dataView.getUint16(0) === 0xffd8) {
31919                     while (offset < maxOffset) {
31920                         markerBytes = dataView.getUint16(offset);
31921                         
31922                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31923                             markerLength = dataView.getUint16(offset + 2) + 2;
31924                             if (offset + markerLength > dataView.byteLength) {
31925                                 Roo.log('Invalid meta data: Invalid segment size.');
31926                                 break;
31927                             }
31928                             
31929                             if(markerBytes == 0xffe1){
31930                                 _this.parseExifData(
31931                                     dataView,
31932                                     offset,
31933                                     markerLength
31934                                 );
31935                             }
31936                             
31937                             offset += markerLength;
31938                             
31939                             continue;
31940                         }
31941                         
31942                         break;
31943                     }
31944                     
31945                 }
31946                 
31947                 var url = _this.urlAPI.createObjectURL(_this.file);
31948                 
31949                 _this.loadCanvas(url);
31950                 
31951                 return;
31952             }
31953             
31954             reader.readAsArrayBuffer(this.file);
31955             
31956         }
31957         
31958     },
31959     
31960     parseExifData : function(dataView, offset, length)
31961     {
31962         var tiffOffset = offset + 10,
31963             littleEndian,
31964             dirOffset;
31965     
31966         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31967             // No Exif data, might be XMP data instead
31968             return;
31969         }
31970         
31971         // Check for the ASCII code for "Exif" (0x45786966):
31972         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31973             // No Exif data, might be XMP data instead
31974             return;
31975         }
31976         if (tiffOffset + 8 > dataView.byteLength) {
31977             Roo.log('Invalid Exif data: Invalid segment size.');
31978             return;
31979         }
31980         // Check for the two null bytes:
31981         if (dataView.getUint16(offset + 8) !== 0x0000) {
31982             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31983             return;
31984         }
31985         // Check the byte alignment:
31986         switch (dataView.getUint16(tiffOffset)) {
31987         case 0x4949:
31988             littleEndian = true;
31989             break;
31990         case 0x4D4D:
31991             littleEndian = false;
31992             break;
31993         default:
31994             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31995             return;
31996         }
31997         // Check for the TIFF tag marker (0x002A):
31998         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31999             Roo.log('Invalid Exif data: Missing TIFF marker.');
32000             return;
32001         }
32002         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32003         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32004         
32005         this.parseExifTags(
32006             dataView,
32007             tiffOffset,
32008             tiffOffset + dirOffset,
32009             littleEndian
32010         );
32011     },
32012     
32013     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32014     {
32015         var tagsNumber,
32016             dirEndOffset,
32017             i;
32018         if (dirOffset + 6 > dataView.byteLength) {
32019             Roo.log('Invalid Exif data: Invalid directory offset.');
32020             return;
32021         }
32022         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32023         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32024         if (dirEndOffset + 4 > dataView.byteLength) {
32025             Roo.log('Invalid Exif data: Invalid directory size.');
32026             return;
32027         }
32028         for (i = 0; i < tagsNumber; i += 1) {
32029             this.parseExifTag(
32030                 dataView,
32031                 tiffOffset,
32032                 dirOffset + 2 + 12 * i, // tag offset
32033                 littleEndian
32034             );
32035         }
32036         // Return the offset to the next directory:
32037         return dataView.getUint32(dirEndOffset, littleEndian);
32038     },
32039     
32040     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32041     {
32042         var tag = dataView.getUint16(offset, littleEndian);
32043         
32044         this.exif[tag] = this.getExifValue(
32045             dataView,
32046             tiffOffset,
32047             offset,
32048             dataView.getUint16(offset + 2, littleEndian), // tag type
32049             dataView.getUint32(offset + 4, littleEndian), // tag length
32050             littleEndian
32051         );
32052     },
32053     
32054     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32055     {
32056         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32057             tagSize,
32058             dataOffset,
32059             values,
32060             i,
32061             str,
32062             c;
32063     
32064         if (!tagType) {
32065             Roo.log('Invalid Exif data: Invalid tag type.');
32066             return;
32067         }
32068         
32069         tagSize = tagType.size * length;
32070         // Determine if the value is contained in the dataOffset bytes,
32071         // or if the value at the dataOffset is a pointer to the actual data:
32072         dataOffset = tagSize > 4 ?
32073                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32074         if (dataOffset + tagSize > dataView.byteLength) {
32075             Roo.log('Invalid Exif data: Invalid data offset.');
32076             return;
32077         }
32078         if (length === 1) {
32079             return tagType.getValue(dataView, dataOffset, littleEndian);
32080         }
32081         values = [];
32082         for (i = 0; i < length; i += 1) {
32083             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32084         }
32085         
32086         if (tagType.ascii) {
32087             str = '';
32088             // Concatenate the chars:
32089             for (i = 0; i < values.length; i += 1) {
32090                 c = values[i];
32091                 // Ignore the terminating NULL byte(s):
32092                 if (c === '\u0000') {
32093                     break;
32094                 }
32095                 str += c;
32096             }
32097             return str;
32098         }
32099         return values;
32100     }
32101     
32102 });
32103
32104 Roo.apply(Roo.bootstrap.UploadCropbox, {
32105     tags : {
32106         'Orientation': 0x0112
32107     },
32108     
32109     Orientation: {
32110             1: 0, //'top-left',
32111 //            2: 'top-right',
32112             3: 180, //'bottom-right',
32113 //            4: 'bottom-left',
32114 //            5: 'left-top',
32115             6: 90, //'right-top',
32116 //            7: 'right-bottom',
32117             8: 270 //'left-bottom'
32118     },
32119     
32120     exifTagTypes : {
32121         // byte, 8-bit unsigned int:
32122         1: {
32123             getValue: function (dataView, dataOffset) {
32124                 return dataView.getUint8(dataOffset);
32125             },
32126             size: 1
32127         },
32128         // ascii, 8-bit byte:
32129         2: {
32130             getValue: function (dataView, dataOffset) {
32131                 return String.fromCharCode(dataView.getUint8(dataOffset));
32132             },
32133             size: 1,
32134             ascii: true
32135         },
32136         // short, 16 bit int:
32137         3: {
32138             getValue: function (dataView, dataOffset, littleEndian) {
32139                 return dataView.getUint16(dataOffset, littleEndian);
32140             },
32141             size: 2
32142         },
32143         // long, 32 bit int:
32144         4: {
32145             getValue: function (dataView, dataOffset, littleEndian) {
32146                 return dataView.getUint32(dataOffset, littleEndian);
32147             },
32148             size: 4
32149         },
32150         // rational = two long values, first is numerator, second is denominator:
32151         5: {
32152             getValue: function (dataView, dataOffset, littleEndian) {
32153                 return dataView.getUint32(dataOffset, littleEndian) /
32154                     dataView.getUint32(dataOffset + 4, littleEndian);
32155             },
32156             size: 8
32157         },
32158         // slong, 32 bit signed int:
32159         9: {
32160             getValue: function (dataView, dataOffset, littleEndian) {
32161                 return dataView.getInt32(dataOffset, littleEndian);
32162             },
32163             size: 4
32164         },
32165         // srational, two slongs, first is numerator, second is denominator:
32166         10: {
32167             getValue: function (dataView, dataOffset, littleEndian) {
32168                 return dataView.getInt32(dataOffset, littleEndian) /
32169                     dataView.getInt32(dataOffset + 4, littleEndian);
32170             },
32171             size: 8
32172         }
32173     },
32174     
32175     footer : {
32176         STANDARD : [
32177             {
32178                 tag : 'div',
32179                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32180                 action : 'rotate-left',
32181                 cn : [
32182                     {
32183                         tag : 'button',
32184                         cls : 'btn btn-default',
32185                         html : '<i class="fa fa-undo"></i>'
32186                     }
32187                 ]
32188             },
32189             {
32190                 tag : 'div',
32191                 cls : 'btn-group roo-upload-cropbox-picture',
32192                 action : 'picture',
32193                 cn : [
32194                     {
32195                         tag : 'button',
32196                         cls : 'btn btn-default',
32197                         html : '<i class="fa fa-picture-o"></i>'
32198                     }
32199                 ]
32200             },
32201             {
32202                 tag : 'div',
32203                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32204                 action : 'rotate-right',
32205                 cn : [
32206                     {
32207                         tag : 'button',
32208                         cls : 'btn btn-default',
32209                         html : '<i class="fa fa-repeat"></i>'
32210                     }
32211                 ]
32212             }
32213         ],
32214         DOCUMENT : [
32215             {
32216                 tag : 'div',
32217                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32218                 action : 'rotate-left',
32219                 cn : [
32220                     {
32221                         tag : 'button',
32222                         cls : 'btn btn-default',
32223                         html : '<i class="fa fa-undo"></i>'
32224                     }
32225                 ]
32226             },
32227             {
32228                 tag : 'div',
32229                 cls : 'btn-group roo-upload-cropbox-download',
32230                 action : 'download',
32231                 cn : [
32232                     {
32233                         tag : 'button',
32234                         cls : 'btn btn-default',
32235                         html : '<i class="fa fa-download"></i>'
32236                     }
32237                 ]
32238             },
32239             {
32240                 tag : 'div',
32241                 cls : 'btn-group roo-upload-cropbox-crop',
32242                 action : 'crop',
32243                 cn : [
32244                     {
32245                         tag : 'button',
32246                         cls : 'btn btn-default',
32247                         html : '<i class="fa fa-crop"></i>'
32248                     }
32249                 ]
32250             },
32251             {
32252                 tag : 'div',
32253                 cls : 'btn-group roo-upload-cropbox-trash',
32254                 action : 'trash',
32255                 cn : [
32256                     {
32257                         tag : 'button',
32258                         cls : 'btn btn-default',
32259                         html : '<i class="fa fa-trash"></i>'
32260                     }
32261                 ]
32262             },
32263             {
32264                 tag : 'div',
32265                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32266                 action : 'rotate-right',
32267                 cn : [
32268                     {
32269                         tag : 'button',
32270                         cls : 'btn btn-default',
32271                         html : '<i class="fa fa-repeat"></i>'
32272                     }
32273                 ]
32274             }
32275         ],
32276         ROTATOR : [
32277             {
32278                 tag : 'div',
32279                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32280                 action : 'rotate-left',
32281                 cn : [
32282                     {
32283                         tag : 'button',
32284                         cls : 'btn btn-default',
32285                         html : '<i class="fa fa-undo"></i>'
32286                     }
32287                 ]
32288             },
32289             {
32290                 tag : 'div',
32291                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32292                 action : 'rotate-right',
32293                 cn : [
32294                     {
32295                         tag : 'button',
32296                         cls : 'btn btn-default',
32297                         html : '<i class="fa fa-repeat"></i>'
32298                     }
32299                 ]
32300             }
32301         ]
32302     }
32303 });
32304
32305 /*
32306 * Licence: LGPL
32307 */
32308
32309 /**
32310  * @class Roo.bootstrap.DocumentManager
32311  * @extends Roo.bootstrap.Component
32312  * Bootstrap DocumentManager class
32313  * @cfg {String} paramName default 'imageUpload'
32314  * @cfg {String} toolTipName default 'filename'
32315  * @cfg {String} method default POST
32316  * @cfg {String} url action url
32317  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32318  * @cfg {Boolean} multiple multiple upload default true
32319  * @cfg {Number} thumbSize default 300
32320  * @cfg {String} fieldLabel
32321  * @cfg {Number} labelWidth default 4
32322  * @cfg {String} labelAlign (left|top) default left
32323  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32324 * @cfg {Number} labellg set the width of label (1-12)
32325  * @cfg {Number} labelmd set the width of label (1-12)
32326  * @cfg {Number} labelsm set the width of label (1-12)
32327  * @cfg {Number} labelxs set the width of label (1-12)
32328  * 
32329  * @constructor
32330  * Create a new DocumentManager
32331  * @param {Object} config The config object
32332  */
32333
32334 Roo.bootstrap.DocumentManager = function(config){
32335     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32336     
32337     this.files = [];
32338     this.delegates = [];
32339     
32340     this.addEvents({
32341         /**
32342          * @event initial
32343          * Fire when initial the DocumentManager
32344          * @param {Roo.bootstrap.DocumentManager} this
32345          */
32346         "initial" : true,
32347         /**
32348          * @event inspect
32349          * inspect selected file
32350          * @param {Roo.bootstrap.DocumentManager} this
32351          * @param {File} file
32352          */
32353         "inspect" : true,
32354         /**
32355          * @event exception
32356          * Fire when xhr load exception
32357          * @param {Roo.bootstrap.DocumentManager} this
32358          * @param {XMLHttpRequest} xhr
32359          */
32360         "exception" : true,
32361         /**
32362          * @event afterupload
32363          * Fire when xhr load exception
32364          * @param {Roo.bootstrap.DocumentManager} this
32365          * @param {XMLHttpRequest} xhr
32366          */
32367         "afterupload" : true,
32368         /**
32369          * @event prepare
32370          * prepare the form data
32371          * @param {Roo.bootstrap.DocumentManager} this
32372          * @param {Object} formData
32373          */
32374         "prepare" : true,
32375         /**
32376          * @event remove
32377          * Fire when remove the file
32378          * @param {Roo.bootstrap.DocumentManager} this
32379          * @param {Object} file
32380          */
32381         "remove" : true,
32382         /**
32383          * @event refresh
32384          * Fire after refresh the file
32385          * @param {Roo.bootstrap.DocumentManager} this
32386          */
32387         "refresh" : true,
32388         /**
32389          * @event click
32390          * Fire after click the image
32391          * @param {Roo.bootstrap.DocumentManager} this
32392          * @param {Object} file
32393          */
32394         "click" : true,
32395         /**
32396          * @event edit
32397          * Fire when upload a image and editable set to true
32398          * @param {Roo.bootstrap.DocumentManager} this
32399          * @param {Object} file
32400          */
32401         "edit" : true,
32402         /**
32403          * @event beforeselectfile
32404          * Fire before select file
32405          * @param {Roo.bootstrap.DocumentManager} this
32406          */
32407         "beforeselectfile" : true,
32408         /**
32409          * @event process
32410          * Fire before process file
32411          * @param {Roo.bootstrap.DocumentManager} this
32412          * @param {Object} file
32413          */
32414         "process" : true,
32415         /**
32416          * @event previewrendered
32417          * Fire when preview rendered
32418          * @param {Roo.bootstrap.DocumentManager} this
32419          * @param {Object} file
32420          */
32421         "previewrendered" : true,
32422         /**
32423          */
32424         "previewResize" : true
32425         
32426     });
32427 };
32428
32429 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32430     
32431     boxes : 0,
32432     inputName : '',
32433     thumbSize : 300,
32434     multiple : true,
32435     files : false,
32436     method : 'POST',
32437     url : '',
32438     paramName : 'imageUpload',
32439     toolTipName : 'filename',
32440     fieldLabel : '',
32441     labelWidth : 4,
32442     labelAlign : 'left',
32443     editable : true,
32444     delegates : false,
32445     xhr : false, 
32446     
32447     labellg : 0,
32448     labelmd : 0,
32449     labelsm : 0,
32450     labelxs : 0,
32451     
32452     getAutoCreate : function()
32453     {   
32454         var managerWidget = {
32455             tag : 'div',
32456             cls : 'roo-document-manager',
32457             cn : [
32458                 {
32459                     tag : 'input',
32460                     cls : 'roo-document-manager-selector',
32461                     type : 'file'
32462                 },
32463                 {
32464                     tag : 'div',
32465                     cls : 'roo-document-manager-uploader',
32466                     cn : [
32467                         {
32468                             tag : 'div',
32469                             cls : 'roo-document-manager-upload-btn',
32470                             html : '<i class="fa fa-plus"></i>'
32471                         }
32472                     ]
32473                     
32474                 }
32475             ]
32476         };
32477         
32478         var content = [
32479             {
32480                 tag : 'div',
32481                 cls : 'column col-md-12',
32482                 cn : managerWidget
32483             }
32484         ];
32485         
32486         if(this.fieldLabel.length){
32487             
32488             content = [
32489                 {
32490                     tag : 'div',
32491                     cls : 'column col-md-12',
32492                     html : this.fieldLabel
32493                 },
32494                 {
32495                     tag : 'div',
32496                     cls : 'column col-md-12',
32497                     cn : managerWidget
32498                 }
32499             ];
32500
32501             if(this.labelAlign == 'left'){
32502                 content = [
32503                     {
32504                         tag : 'div',
32505                         cls : 'column',
32506                         html : this.fieldLabel
32507                     },
32508                     {
32509                         tag : 'div',
32510                         cls : 'column',
32511                         cn : managerWidget
32512                     }
32513                 ];
32514                 
32515                 if(this.labelWidth > 12){
32516                     content[0].style = "width: " + this.labelWidth + 'px';
32517                 }
32518
32519                 if(this.labelWidth < 13 && this.labelmd == 0){
32520                     this.labelmd = this.labelWidth;
32521                 }
32522
32523                 if(this.labellg > 0){
32524                     content[0].cls += ' col-lg-' + this.labellg;
32525                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32526                 }
32527
32528                 if(this.labelmd > 0){
32529                     content[0].cls += ' col-md-' + this.labelmd;
32530                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32531                 }
32532
32533                 if(this.labelsm > 0){
32534                     content[0].cls += ' col-sm-' + this.labelsm;
32535                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32536                 }
32537
32538                 if(this.labelxs > 0){
32539                     content[0].cls += ' col-xs-' + this.labelxs;
32540                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32541                 }
32542                 
32543             }
32544         }
32545         
32546         var cfg = {
32547             tag : 'div',
32548             cls : 'row clearfix',
32549             cn : content
32550         };
32551         
32552         return cfg;
32553         
32554     },
32555     
32556     initEvents : function()
32557     {
32558         this.managerEl = this.el.select('.roo-document-manager', true).first();
32559         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32560         
32561         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32562         this.selectorEl.hide();
32563         
32564         if(this.multiple){
32565             this.selectorEl.attr('multiple', 'multiple');
32566         }
32567         
32568         this.selectorEl.on('change', this.onFileSelected, this);
32569         
32570         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32571         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32572         
32573         this.uploader.on('click', this.onUploaderClick, this);
32574         
32575         this.renderProgressDialog();
32576         
32577         var _this = this;
32578         
32579         window.addEventListener("resize", function() { _this.refresh(); } );
32580         
32581         this.fireEvent('initial', this);
32582     },
32583     
32584     renderProgressDialog : function()
32585     {
32586         var _this = this;
32587         
32588         this.progressDialog = new Roo.bootstrap.Modal({
32589             cls : 'roo-document-manager-progress-dialog',
32590             allow_close : false,
32591             animate : false,
32592             title : '',
32593             buttons : [
32594                 {
32595                     name  :'cancel',
32596                     weight : 'danger',
32597                     html : 'Cancel'
32598                 }
32599             ], 
32600             listeners : { 
32601                 btnclick : function() {
32602                     _this.uploadCancel();
32603                     this.hide();
32604                 }
32605             }
32606         });
32607          
32608         this.progressDialog.render(Roo.get(document.body));
32609          
32610         this.progress = new Roo.bootstrap.Progress({
32611             cls : 'roo-document-manager-progress',
32612             active : true,
32613             striped : true
32614         });
32615         
32616         this.progress.render(this.progressDialog.getChildContainer());
32617         
32618         this.progressBar = new Roo.bootstrap.ProgressBar({
32619             cls : 'roo-document-manager-progress-bar',
32620             aria_valuenow : 0,
32621             aria_valuemin : 0,
32622             aria_valuemax : 12,
32623             panel : 'success'
32624         });
32625         
32626         this.progressBar.render(this.progress.getChildContainer());
32627     },
32628     
32629     onUploaderClick : function(e)
32630     {
32631         e.preventDefault();
32632      
32633         if(this.fireEvent('beforeselectfile', this) != false){
32634             this.selectorEl.dom.click();
32635         }
32636         
32637     },
32638     
32639     onFileSelected : function(e)
32640     {
32641         e.preventDefault();
32642         
32643         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32644             return;
32645         }
32646         
32647         Roo.each(this.selectorEl.dom.files, function(file){
32648             if(this.fireEvent('inspect', this, file) != false){
32649                 this.files.push(file);
32650             }
32651         }, this);
32652         
32653         this.queue();
32654         
32655     },
32656     
32657     queue : function()
32658     {
32659         this.selectorEl.dom.value = '';
32660         
32661         if(!this.files || !this.files.length){
32662             return;
32663         }
32664         
32665         if(this.boxes > 0 && this.files.length > this.boxes){
32666             this.files = this.files.slice(0, this.boxes);
32667         }
32668         
32669         this.uploader.show();
32670         
32671         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32672             this.uploader.hide();
32673         }
32674         
32675         var _this = this;
32676         
32677         var files = [];
32678         
32679         var docs = [];
32680         
32681         Roo.each(this.files, function(file){
32682             
32683             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32684                 var f = this.renderPreview(file);
32685                 files.push(f);
32686                 return;
32687             }
32688             
32689             if(file.type.indexOf('image') != -1){
32690                 this.delegates.push(
32691                     (function(){
32692                         _this.process(file);
32693                     }).createDelegate(this)
32694                 );
32695         
32696                 return;
32697             }
32698             
32699             docs.push(
32700                 (function(){
32701                     _this.process(file);
32702                 }).createDelegate(this)
32703             );
32704             
32705         }, this);
32706         
32707         this.files = files;
32708         
32709         this.delegates = this.delegates.concat(docs);
32710         
32711         if(!this.delegates.length){
32712             this.refresh();
32713             return;
32714         }
32715         
32716         this.progressBar.aria_valuemax = this.delegates.length;
32717         
32718         this.arrange();
32719         
32720         return;
32721     },
32722     
32723     arrange : function()
32724     {
32725         if(!this.delegates.length){
32726             this.progressDialog.hide();
32727             this.refresh();
32728             return;
32729         }
32730         
32731         var delegate = this.delegates.shift();
32732         
32733         this.progressDialog.show();
32734         
32735         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32736         
32737         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32738         
32739         delegate();
32740     },
32741     
32742     refresh : function()
32743     {
32744         this.uploader.show();
32745         
32746         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32747             this.uploader.hide();
32748         }
32749         
32750         Roo.isTouch ? this.closable(false) : this.closable(true);
32751         
32752         this.fireEvent('refresh', this);
32753     },
32754     
32755     onRemove : function(e, el, o)
32756     {
32757         e.preventDefault();
32758         
32759         this.fireEvent('remove', this, o);
32760         
32761     },
32762     
32763     remove : function(o)
32764     {
32765         var files = [];
32766         
32767         Roo.each(this.files, function(file){
32768             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32769                 files.push(file);
32770                 return;
32771             }
32772
32773             o.target.remove();
32774
32775         }, this);
32776         
32777         this.files = files;
32778         
32779         this.refresh();
32780     },
32781     
32782     clear : function()
32783     {
32784         Roo.each(this.files, function(file){
32785             if(!file.target){
32786                 return;
32787             }
32788             
32789             file.target.remove();
32790
32791         }, this);
32792         
32793         this.files = [];
32794         
32795         this.refresh();
32796     },
32797     
32798     onClick : function(e, el, o)
32799     {
32800         e.preventDefault();
32801         
32802         this.fireEvent('click', this, o);
32803         
32804     },
32805     
32806     closable : function(closable)
32807     {
32808         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32809             
32810             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32811             
32812             if(closable){
32813                 el.show();
32814                 return;
32815             }
32816             
32817             el.hide();
32818             
32819         }, this);
32820     },
32821     
32822     xhrOnLoad : function(xhr)
32823     {
32824         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32825             el.remove();
32826         }, this);
32827         
32828         if (xhr.readyState !== 4) {
32829             this.arrange();
32830             this.fireEvent('exception', this, xhr);
32831             return;
32832         }
32833
32834         var response = Roo.decode(xhr.responseText);
32835         
32836         if(!response.success){
32837             this.arrange();
32838             this.fireEvent('exception', this, xhr);
32839             return;
32840         }
32841         
32842         var file = this.renderPreview(response.data);
32843         
32844         this.files.push(file);
32845         
32846         this.arrange();
32847         
32848         this.fireEvent('afterupload', this, xhr);
32849         
32850     },
32851     
32852     xhrOnError : function(xhr)
32853     {
32854         Roo.log('xhr on error');
32855         
32856         var response = Roo.decode(xhr.responseText);
32857           
32858         Roo.log(response);
32859         
32860         this.arrange();
32861     },
32862     
32863     process : function(file)
32864     {
32865         if(this.fireEvent('process', this, file) !== false){
32866             if(this.editable && file.type.indexOf('image') != -1){
32867                 this.fireEvent('edit', this, file);
32868                 return;
32869             }
32870
32871             this.uploadStart(file, false);
32872
32873             return;
32874         }
32875         
32876     },
32877     
32878     uploadStart : function(file, crop)
32879     {
32880         this.xhr = new XMLHttpRequest();
32881         
32882         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32883             this.arrange();
32884             return;
32885         }
32886         
32887         file.xhr = this.xhr;
32888             
32889         this.managerEl.createChild({
32890             tag : 'div',
32891             cls : 'roo-document-manager-loading',
32892             cn : [
32893                 {
32894                     tag : 'div',
32895                     tooltip : file.name,
32896                     cls : 'roo-document-manager-thumb',
32897                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32898                 }
32899             ]
32900
32901         });
32902
32903         this.xhr.open(this.method, this.url, true);
32904         
32905         var headers = {
32906             "Accept": "application/json",
32907             "Cache-Control": "no-cache",
32908             "X-Requested-With": "XMLHttpRequest"
32909         };
32910         
32911         for (var headerName in headers) {
32912             var headerValue = headers[headerName];
32913             if (headerValue) {
32914                 this.xhr.setRequestHeader(headerName, headerValue);
32915             }
32916         }
32917         
32918         var _this = this;
32919         
32920         this.xhr.onload = function()
32921         {
32922             _this.xhrOnLoad(_this.xhr);
32923         }
32924         
32925         this.xhr.onerror = function()
32926         {
32927             _this.xhrOnError(_this.xhr);
32928         }
32929         
32930         var formData = new FormData();
32931
32932         formData.append('returnHTML', 'NO');
32933         
32934         if(crop){
32935             formData.append('crop', crop);
32936         }
32937         
32938         formData.append(this.paramName, file, file.name);
32939         
32940         var options = {
32941             file : file, 
32942             manually : false
32943         };
32944         
32945         if(this.fireEvent('prepare', this, formData, options) != false){
32946             
32947             if(options.manually){
32948                 return;
32949             }
32950             
32951             this.xhr.send(formData);
32952             return;
32953         };
32954         
32955         this.uploadCancel();
32956     },
32957     
32958     uploadCancel : function()
32959     {
32960         if (this.xhr) {
32961             this.xhr.abort();
32962         }
32963         
32964         this.delegates = [];
32965         
32966         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32967             el.remove();
32968         }, this);
32969         
32970         this.arrange();
32971     },
32972     
32973     renderPreview : function(file)
32974     {
32975         if(typeof(file.target) != 'undefined' && file.target){
32976             return file;
32977         }
32978         
32979         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32980         
32981         var previewEl = this.managerEl.createChild({
32982             tag : 'div',
32983             cls : 'roo-document-manager-preview',
32984             cn : [
32985                 {
32986                     tag : 'div',
32987                     tooltip : file[this.toolTipName],
32988                     cls : 'roo-document-manager-thumb',
32989                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32990                 },
32991                 {
32992                     tag : 'button',
32993                     cls : 'close',
32994                     html : '<i class="fa fa-times-circle"></i>'
32995                 }
32996             ]
32997         });
32998
32999         var close = previewEl.select('button.close', true).first();
33000
33001         close.on('click', this.onRemove, this, file);
33002
33003         file.target = previewEl;
33004
33005         var image = previewEl.select('img', true).first();
33006         
33007         var _this = this;
33008         
33009         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33010         
33011         image.on('click', this.onClick, this, file);
33012         
33013         this.fireEvent('previewrendered', this, file);
33014         
33015         return file;
33016         
33017     },
33018     
33019     onPreviewLoad : function(file, image)
33020     {
33021         if(typeof(file.target) == 'undefined' || !file.target){
33022             return;
33023         }
33024         
33025         var width = image.dom.naturalWidth || image.dom.width;
33026         var height = image.dom.naturalHeight || image.dom.height;
33027         
33028         if(!this.previewResize) {
33029             return;
33030         }
33031         
33032         if(width > height){
33033             file.target.addClass('wide');
33034             return;
33035         }
33036         
33037         file.target.addClass('tall');
33038         return;
33039         
33040     },
33041     
33042     uploadFromSource : function(file, crop)
33043     {
33044         this.xhr = new XMLHttpRequest();
33045         
33046         this.managerEl.createChild({
33047             tag : 'div',
33048             cls : 'roo-document-manager-loading',
33049             cn : [
33050                 {
33051                     tag : 'div',
33052                     tooltip : file.name,
33053                     cls : 'roo-document-manager-thumb',
33054                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33055                 }
33056             ]
33057
33058         });
33059
33060         this.xhr.open(this.method, this.url, true);
33061         
33062         var headers = {
33063             "Accept": "application/json",
33064             "Cache-Control": "no-cache",
33065             "X-Requested-With": "XMLHttpRequest"
33066         };
33067         
33068         for (var headerName in headers) {
33069             var headerValue = headers[headerName];
33070             if (headerValue) {
33071                 this.xhr.setRequestHeader(headerName, headerValue);
33072             }
33073         }
33074         
33075         var _this = this;
33076         
33077         this.xhr.onload = function()
33078         {
33079             _this.xhrOnLoad(_this.xhr);
33080         }
33081         
33082         this.xhr.onerror = function()
33083         {
33084             _this.xhrOnError(_this.xhr);
33085         }
33086         
33087         var formData = new FormData();
33088
33089         formData.append('returnHTML', 'NO');
33090         
33091         formData.append('crop', crop);
33092         
33093         if(typeof(file.filename) != 'undefined'){
33094             formData.append('filename', file.filename);
33095         }
33096         
33097         if(typeof(file.mimetype) != 'undefined'){
33098             formData.append('mimetype', file.mimetype);
33099         }
33100         
33101         Roo.log(formData);
33102         
33103         if(this.fireEvent('prepare', this, formData) != false){
33104             this.xhr.send(formData);
33105         };
33106     }
33107 });
33108
33109 /*
33110 * Licence: LGPL
33111 */
33112
33113 /**
33114  * @class Roo.bootstrap.DocumentViewer
33115  * @extends Roo.bootstrap.Component
33116  * Bootstrap DocumentViewer class
33117  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33118  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33119  * 
33120  * @constructor
33121  * Create a new DocumentViewer
33122  * @param {Object} config The config object
33123  */
33124
33125 Roo.bootstrap.DocumentViewer = function(config){
33126     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33127     
33128     this.addEvents({
33129         /**
33130          * @event initial
33131          * Fire after initEvent
33132          * @param {Roo.bootstrap.DocumentViewer} this
33133          */
33134         "initial" : true,
33135         /**
33136          * @event click
33137          * Fire after click
33138          * @param {Roo.bootstrap.DocumentViewer} this
33139          */
33140         "click" : true,
33141         /**
33142          * @event download
33143          * Fire after download button
33144          * @param {Roo.bootstrap.DocumentViewer} this
33145          */
33146         "download" : true,
33147         /**
33148          * @event trash
33149          * Fire after trash button
33150          * @param {Roo.bootstrap.DocumentViewer} this
33151          */
33152         "trash" : true
33153         
33154     });
33155 };
33156
33157 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33158     
33159     showDownload : true,
33160     
33161     showTrash : true,
33162     
33163     getAutoCreate : function()
33164     {
33165         var cfg = {
33166             tag : 'div',
33167             cls : 'roo-document-viewer',
33168             cn : [
33169                 {
33170                     tag : 'div',
33171                     cls : 'roo-document-viewer-body',
33172                     cn : [
33173                         {
33174                             tag : 'div',
33175                             cls : 'roo-document-viewer-thumb',
33176                             cn : [
33177                                 {
33178                                     tag : 'img',
33179                                     cls : 'roo-document-viewer-image'
33180                                 }
33181                             ]
33182                         }
33183                     ]
33184                 },
33185                 {
33186                     tag : 'div',
33187                     cls : 'roo-document-viewer-footer',
33188                     cn : {
33189                         tag : 'div',
33190                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33191                         cn : [
33192                             {
33193                                 tag : 'div',
33194                                 cls : 'btn-group roo-document-viewer-download',
33195                                 cn : [
33196                                     {
33197                                         tag : 'button',
33198                                         cls : 'btn btn-default',
33199                                         html : '<i class="fa fa-download"></i>'
33200                                     }
33201                                 ]
33202                             },
33203                             {
33204                                 tag : 'div',
33205                                 cls : 'btn-group roo-document-viewer-trash',
33206                                 cn : [
33207                                     {
33208                                         tag : 'button',
33209                                         cls : 'btn btn-default',
33210                                         html : '<i class="fa fa-trash"></i>'
33211                                     }
33212                                 ]
33213                             }
33214                         ]
33215                     }
33216                 }
33217             ]
33218         };
33219         
33220         return cfg;
33221     },
33222     
33223     initEvents : function()
33224     {
33225         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33226         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33227         
33228         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33229         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33230         
33231         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33232         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33233         
33234         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33235         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33236         
33237         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33238         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33239         
33240         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33241         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33242         
33243         this.bodyEl.on('click', this.onClick, this);
33244         this.downloadBtn.on('click', this.onDownload, this);
33245         this.trashBtn.on('click', this.onTrash, this);
33246         
33247         this.downloadBtn.hide();
33248         this.trashBtn.hide();
33249         
33250         if(this.showDownload){
33251             this.downloadBtn.show();
33252         }
33253         
33254         if(this.showTrash){
33255             this.trashBtn.show();
33256         }
33257         
33258         if(!this.showDownload && !this.showTrash) {
33259             this.footerEl.hide();
33260         }
33261         
33262     },
33263     
33264     initial : function()
33265     {
33266         this.fireEvent('initial', this);
33267         
33268     },
33269     
33270     onClick : function(e)
33271     {
33272         e.preventDefault();
33273         
33274         this.fireEvent('click', this);
33275     },
33276     
33277     onDownload : function(e)
33278     {
33279         e.preventDefault();
33280         
33281         this.fireEvent('download', this);
33282     },
33283     
33284     onTrash : function(e)
33285     {
33286         e.preventDefault();
33287         
33288         this.fireEvent('trash', this);
33289     }
33290     
33291 });
33292 /*
33293  * - LGPL
33294  *
33295  * nav progress bar
33296  * 
33297  */
33298
33299 /**
33300  * @class Roo.bootstrap.NavProgressBar
33301  * @extends Roo.bootstrap.Component
33302  * Bootstrap NavProgressBar class
33303  * 
33304  * @constructor
33305  * Create a new nav progress bar
33306  * @param {Object} config The config object
33307  */
33308
33309 Roo.bootstrap.NavProgressBar = function(config){
33310     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33311
33312     this.bullets = this.bullets || [];
33313    
33314 //    Roo.bootstrap.NavProgressBar.register(this);
33315      this.addEvents({
33316         /**
33317              * @event changed
33318              * Fires when the active item changes
33319              * @param {Roo.bootstrap.NavProgressBar} this
33320              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33321              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33322          */
33323         'changed': true
33324      });
33325     
33326 };
33327
33328 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33329     
33330     bullets : [],
33331     barItems : [],
33332     
33333     getAutoCreate : function()
33334     {
33335         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33336         
33337         cfg = {
33338             tag : 'div',
33339             cls : 'roo-navigation-bar-group',
33340             cn : [
33341                 {
33342                     tag : 'div',
33343                     cls : 'roo-navigation-top-bar'
33344                 },
33345                 {
33346                     tag : 'div',
33347                     cls : 'roo-navigation-bullets-bar',
33348                     cn : [
33349                         {
33350                             tag : 'ul',
33351                             cls : 'roo-navigation-bar'
33352                         }
33353                     ]
33354                 },
33355                 
33356                 {
33357                     tag : 'div',
33358                     cls : 'roo-navigation-bottom-bar'
33359                 }
33360             ]
33361             
33362         };
33363         
33364         return cfg;
33365         
33366     },
33367     
33368     initEvents: function() 
33369     {
33370         
33371     },
33372     
33373     onRender : function(ct, position) 
33374     {
33375         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33376         
33377         if(this.bullets.length){
33378             Roo.each(this.bullets, function(b){
33379                this.addItem(b);
33380             }, this);
33381         }
33382         
33383         this.format();
33384         
33385     },
33386     
33387     addItem : function(cfg)
33388     {
33389         var item = new Roo.bootstrap.NavProgressItem(cfg);
33390         
33391         item.parentId = this.id;
33392         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33393         
33394         if(cfg.html){
33395             var top = new Roo.bootstrap.Element({
33396                 tag : 'div',
33397                 cls : 'roo-navigation-bar-text'
33398             });
33399             
33400             var bottom = new Roo.bootstrap.Element({
33401                 tag : 'div',
33402                 cls : 'roo-navigation-bar-text'
33403             });
33404             
33405             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33406             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33407             
33408             var topText = new Roo.bootstrap.Element({
33409                 tag : 'span',
33410                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33411             });
33412             
33413             var bottomText = new Roo.bootstrap.Element({
33414                 tag : 'span',
33415                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33416             });
33417             
33418             topText.onRender(top.el, null);
33419             bottomText.onRender(bottom.el, null);
33420             
33421             item.topEl = top;
33422             item.bottomEl = bottom;
33423         }
33424         
33425         this.barItems.push(item);
33426         
33427         return item;
33428     },
33429     
33430     getActive : function()
33431     {
33432         var active = false;
33433         
33434         Roo.each(this.barItems, function(v){
33435             
33436             if (!v.isActive()) {
33437                 return;
33438             }
33439             
33440             active = v;
33441             return false;
33442             
33443         });
33444         
33445         return active;
33446     },
33447     
33448     setActiveItem : function(item)
33449     {
33450         var prev = false;
33451         
33452         Roo.each(this.barItems, function(v){
33453             if (v.rid == item.rid) {
33454                 return ;
33455             }
33456             
33457             if (v.isActive()) {
33458                 v.setActive(false);
33459                 prev = v;
33460             }
33461         });
33462
33463         item.setActive(true);
33464         
33465         this.fireEvent('changed', this, item, prev);
33466     },
33467     
33468     getBarItem: function(rid)
33469     {
33470         var ret = false;
33471         
33472         Roo.each(this.barItems, function(e) {
33473             if (e.rid != rid) {
33474                 return;
33475             }
33476             
33477             ret =  e;
33478             return false;
33479         });
33480         
33481         return ret;
33482     },
33483     
33484     indexOfItem : function(item)
33485     {
33486         var index = false;
33487         
33488         Roo.each(this.barItems, function(v, i){
33489             
33490             if (v.rid != item.rid) {
33491                 return;
33492             }
33493             
33494             index = i;
33495             return false
33496         });
33497         
33498         return index;
33499     },
33500     
33501     setActiveNext : function()
33502     {
33503         var i = this.indexOfItem(this.getActive());
33504         
33505         if (i > this.barItems.length) {
33506             return;
33507         }
33508         
33509         this.setActiveItem(this.barItems[i+1]);
33510     },
33511     
33512     setActivePrev : function()
33513     {
33514         var i = this.indexOfItem(this.getActive());
33515         
33516         if (i  < 1) {
33517             return;
33518         }
33519         
33520         this.setActiveItem(this.barItems[i-1]);
33521     },
33522     
33523     format : function()
33524     {
33525         if(!this.barItems.length){
33526             return;
33527         }
33528      
33529         var width = 100 / this.barItems.length;
33530         
33531         Roo.each(this.barItems, function(i){
33532             i.el.setStyle('width', width + '%');
33533             i.topEl.el.setStyle('width', width + '%');
33534             i.bottomEl.el.setStyle('width', width + '%');
33535         }, this);
33536         
33537     }
33538     
33539 });
33540 /*
33541  * - LGPL
33542  *
33543  * Nav Progress Item
33544  * 
33545  */
33546
33547 /**
33548  * @class Roo.bootstrap.NavProgressItem
33549  * @extends Roo.bootstrap.Component
33550  * Bootstrap NavProgressItem class
33551  * @cfg {String} rid the reference id
33552  * @cfg {Boolean} active (true|false) Is item active default false
33553  * @cfg {Boolean} disabled (true|false) Is item active default false
33554  * @cfg {String} html
33555  * @cfg {String} position (top|bottom) text position default bottom
33556  * @cfg {String} icon show icon instead of number
33557  * 
33558  * @constructor
33559  * Create a new NavProgressItem
33560  * @param {Object} config The config object
33561  */
33562 Roo.bootstrap.NavProgressItem = function(config){
33563     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33564     this.addEvents({
33565         // raw events
33566         /**
33567          * @event click
33568          * The raw click event for the entire grid.
33569          * @param {Roo.bootstrap.NavProgressItem} this
33570          * @param {Roo.EventObject} e
33571          */
33572         "click" : true
33573     });
33574    
33575 };
33576
33577 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33578     
33579     rid : '',
33580     active : false,
33581     disabled : false,
33582     html : '',
33583     position : 'bottom',
33584     icon : false,
33585     
33586     getAutoCreate : function()
33587     {
33588         var iconCls = 'roo-navigation-bar-item-icon';
33589         
33590         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33591         
33592         var cfg = {
33593             tag: 'li',
33594             cls: 'roo-navigation-bar-item',
33595             cn : [
33596                 {
33597                     tag : 'i',
33598                     cls : iconCls
33599                 }
33600             ]
33601         };
33602         
33603         if(this.active){
33604             cfg.cls += ' active';
33605         }
33606         if(this.disabled){
33607             cfg.cls += ' disabled';
33608         }
33609         
33610         return cfg;
33611     },
33612     
33613     disable : function()
33614     {
33615         this.setDisabled(true);
33616     },
33617     
33618     enable : function()
33619     {
33620         this.setDisabled(false);
33621     },
33622     
33623     initEvents: function() 
33624     {
33625         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33626         
33627         this.iconEl.on('click', this.onClick, this);
33628     },
33629     
33630     onClick : function(e)
33631     {
33632         e.preventDefault();
33633         
33634         if(this.disabled){
33635             return;
33636         }
33637         
33638         if(this.fireEvent('click', this, e) === false){
33639             return;
33640         };
33641         
33642         this.parent().setActiveItem(this);
33643     },
33644     
33645     isActive: function () 
33646     {
33647         return this.active;
33648     },
33649     
33650     setActive : function(state)
33651     {
33652         if(this.active == state){
33653             return;
33654         }
33655         
33656         this.active = state;
33657         
33658         if (state) {
33659             this.el.addClass('active');
33660             return;
33661         }
33662         
33663         this.el.removeClass('active');
33664         
33665         return;
33666     },
33667     
33668     setDisabled : function(state)
33669     {
33670         if(this.disabled == state){
33671             return;
33672         }
33673         
33674         this.disabled = state;
33675         
33676         if (state) {
33677             this.el.addClass('disabled');
33678             return;
33679         }
33680         
33681         this.el.removeClass('disabled');
33682     },
33683     
33684     tooltipEl : function()
33685     {
33686         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33687     }
33688 });
33689  
33690
33691  /*
33692  * - LGPL
33693  *
33694  * FieldLabel
33695  * 
33696  */
33697
33698 /**
33699  * @class Roo.bootstrap.FieldLabel
33700  * @extends Roo.bootstrap.Component
33701  * Bootstrap FieldLabel class
33702  * @cfg {String} html contents of the element
33703  * @cfg {String} tag tag of the element default label
33704  * @cfg {String} cls class of the element
33705  * @cfg {String} target label target 
33706  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33707  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33708  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33709  * @cfg {String} iconTooltip default "This field is required"
33710  * @cfg {String} indicatorpos (left|right) default left
33711  * 
33712  * @constructor
33713  * Create a new FieldLabel
33714  * @param {Object} config The config object
33715  */
33716
33717 Roo.bootstrap.FieldLabel = function(config){
33718     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33719     
33720     this.addEvents({
33721             /**
33722              * @event invalid
33723              * Fires after the field has been marked as invalid.
33724              * @param {Roo.form.FieldLabel} this
33725              * @param {String} msg The validation message
33726              */
33727             invalid : true,
33728             /**
33729              * @event valid
33730              * Fires after the field has been validated with no errors.
33731              * @param {Roo.form.FieldLabel} this
33732              */
33733             valid : true
33734         });
33735 };
33736
33737 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33738     
33739     tag: 'label',
33740     cls: '',
33741     html: '',
33742     target: '',
33743     allowBlank : true,
33744     invalidClass : 'has-warning',
33745     validClass : 'has-success',
33746     iconTooltip : 'This field is required',
33747     indicatorpos : 'left',
33748     
33749     getAutoCreate : function(){
33750         
33751         var cls = "";
33752         if (!this.allowBlank) {
33753             cls  = "visible";
33754         }
33755         
33756         var cfg = {
33757             tag : this.tag,
33758             cls : 'roo-bootstrap-field-label ' + this.cls,
33759             for : this.target,
33760             cn : [
33761                 {
33762                     tag : 'i',
33763                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33764                     tooltip : this.iconTooltip
33765                 },
33766                 {
33767                     tag : 'span',
33768                     html : this.html
33769                 }
33770             ] 
33771         };
33772         
33773         if(this.indicatorpos == 'right'){
33774             var cfg = {
33775                 tag : this.tag,
33776                 cls : 'roo-bootstrap-field-label ' + this.cls,
33777                 for : this.target,
33778                 cn : [
33779                     {
33780                         tag : 'span',
33781                         html : this.html
33782                     },
33783                     {
33784                         tag : 'i',
33785                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33786                         tooltip : this.iconTooltip
33787                     }
33788                 ] 
33789             };
33790         }
33791         
33792         return cfg;
33793     },
33794     
33795     initEvents: function() 
33796     {
33797         Roo.bootstrap.Element.superclass.initEvents.call(this);
33798         
33799         this.indicator = this.indicatorEl();
33800         
33801         if(this.indicator){
33802             this.indicator.removeClass('visible');
33803             this.indicator.addClass('invisible');
33804         }
33805         
33806         Roo.bootstrap.FieldLabel.register(this);
33807     },
33808     
33809     indicatorEl : function()
33810     {
33811         var indicator = this.el.select('i.roo-required-indicator',true).first();
33812         
33813         if(!indicator){
33814             return false;
33815         }
33816         
33817         return indicator;
33818         
33819     },
33820     
33821     /**
33822      * Mark this field as valid
33823      */
33824     markValid : function()
33825     {
33826         if(this.indicator){
33827             this.indicator.removeClass('visible');
33828             this.indicator.addClass('invisible');
33829         }
33830         if (Roo.bootstrap.version == 3) {
33831             this.el.removeClass(this.invalidClass);
33832             this.el.addClass(this.validClass);
33833         } else {
33834             this.el.removeClass('is-invalid');
33835             this.el.addClass('is-valid');
33836         }
33837         
33838         
33839         this.fireEvent('valid', this);
33840     },
33841     
33842     /**
33843      * Mark this field as invalid
33844      * @param {String} msg The validation message
33845      */
33846     markInvalid : function(msg)
33847     {
33848         if(this.indicator){
33849             this.indicator.removeClass('invisible');
33850             this.indicator.addClass('visible');
33851         }
33852           if (Roo.bootstrap.version == 3) {
33853             this.el.removeClass(this.validClass);
33854             this.el.addClass(this.invalidClass);
33855         } else {
33856             this.el.removeClass('is-valid');
33857             this.el.addClass('is-invalid');
33858         }
33859         
33860         
33861         this.fireEvent('invalid', this, msg);
33862     }
33863     
33864    
33865 });
33866
33867 Roo.apply(Roo.bootstrap.FieldLabel, {
33868     
33869     groups: {},
33870     
33871      /**
33872     * register a FieldLabel Group
33873     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33874     */
33875     register : function(label)
33876     {
33877         if(this.groups.hasOwnProperty(label.target)){
33878             return;
33879         }
33880      
33881         this.groups[label.target] = label;
33882         
33883     },
33884     /**
33885     * fetch a FieldLabel Group based on the target
33886     * @param {string} target
33887     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33888     */
33889     get: function(target) {
33890         if (typeof(this.groups[target]) == 'undefined') {
33891             return false;
33892         }
33893         
33894         return this.groups[target] ;
33895     }
33896 });
33897
33898  
33899
33900  /*
33901  * - LGPL
33902  *
33903  * page DateSplitField.
33904  * 
33905  */
33906
33907
33908 /**
33909  * @class Roo.bootstrap.DateSplitField
33910  * @extends Roo.bootstrap.Component
33911  * Bootstrap DateSplitField class
33912  * @cfg {string} fieldLabel - the label associated
33913  * @cfg {Number} labelWidth set the width of label (0-12)
33914  * @cfg {String} labelAlign (top|left)
33915  * @cfg {Boolean} dayAllowBlank (true|false) default false
33916  * @cfg {Boolean} monthAllowBlank (true|false) default false
33917  * @cfg {Boolean} yearAllowBlank (true|false) default false
33918  * @cfg {string} dayPlaceholder 
33919  * @cfg {string} monthPlaceholder
33920  * @cfg {string} yearPlaceholder
33921  * @cfg {string} dayFormat default 'd'
33922  * @cfg {string} monthFormat default 'm'
33923  * @cfg {string} yearFormat default 'Y'
33924  * @cfg {Number} labellg set the width of label (1-12)
33925  * @cfg {Number} labelmd set the width of label (1-12)
33926  * @cfg {Number} labelsm set the width of label (1-12)
33927  * @cfg {Number} labelxs set the width of label (1-12)
33928
33929  *     
33930  * @constructor
33931  * Create a new DateSplitField
33932  * @param {Object} config The config object
33933  */
33934
33935 Roo.bootstrap.DateSplitField = function(config){
33936     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33937     
33938     this.addEvents({
33939         // raw events
33940          /**
33941          * @event years
33942          * getting the data of years
33943          * @param {Roo.bootstrap.DateSplitField} this
33944          * @param {Object} years
33945          */
33946         "years" : true,
33947         /**
33948          * @event days
33949          * getting the data of days
33950          * @param {Roo.bootstrap.DateSplitField} this
33951          * @param {Object} days
33952          */
33953         "days" : true,
33954         /**
33955          * @event invalid
33956          * Fires after the field has been marked as invalid.
33957          * @param {Roo.form.Field} this
33958          * @param {String} msg The validation message
33959          */
33960         invalid : true,
33961        /**
33962          * @event valid
33963          * Fires after the field has been validated with no errors.
33964          * @param {Roo.form.Field} this
33965          */
33966         valid : true
33967     });
33968 };
33969
33970 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33971     
33972     fieldLabel : '',
33973     labelAlign : 'top',
33974     labelWidth : 3,
33975     dayAllowBlank : false,
33976     monthAllowBlank : false,
33977     yearAllowBlank : false,
33978     dayPlaceholder : '',
33979     monthPlaceholder : '',
33980     yearPlaceholder : '',
33981     dayFormat : 'd',
33982     monthFormat : 'm',
33983     yearFormat : 'Y',
33984     isFormField : true,
33985     labellg : 0,
33986     labelmd : 0,
33987     labelsm : 0,
33988     labelxs : 0,
33989     
33990     getAutoCreate : function()
33991     {
33992         var cfg = {
33993             tag : 'div',
33994             cls : 'row roo-date-split-field-group',
33995             cn : [
33996                 {
33997                     tag : 'input',
33998                     type : 'hidden',
33999                     cls : 'form-hidden-field roo-date-split-field-group-value',
34000                     name : this.name
34001                 }
34002             ]
34003         };
34004         
34005         var labelCls = 'col-md-12';
34006         var contentCls = 'col-md-4';
34007         
34008         if(this.fieldLabel){
34009             
34010             var label = {
34011                 tag : 'div',
34012                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34013                 cn : [
34014                     {
34015                         tag : 'label',
34016                         html : this.fieldLabel
34017                     }
34018                 ]
34019             };
34020             
34021             if(this.labelAlign == 'left'){
34022             
34023                 if(this.labelWidth > 12){
34024                     label.style = "width: " + this.labelWidth + 'px';
34025                 }
34026
34027                 if(this.labelWidth < 13 && this.labelmd == 0){
34028                     this.labelmd = this.labelWidth;
34029                 }
34030
34031                 if(this.labellg > 0){
34032                     labelCls = ' col-lg-' + this.labellg;
34033                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34034                 }
34035
34036                 if(this.labelmd > 0){
34037                     labelCls = ' col-md-' + this.labelmd;
34038                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34039                 }
34040
34041                 if(this.labelsm > 0){
34042                     labelCls = ' col-sm-' + this.labelsm;
34043                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34044                 }
34045
34046                 if(this.labelxs > 0){
34047                     labelCls = ' col-xs-' + this.labelxs;
34048                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34049                 }
34050             }
34051             
34052             label.cls += ' ' + labelCls;
34053             
34054             cfg.cn.push(label);
34055         }
34056         
34057         Roo.each(['day', 'month', 'year'], function(t){
34058             cfg.cn.push({
34059                 tag : 'div',
34060                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34061             });
34062         }, this);
34063         
34064         return cfg;
34065     },
34066     
34067     inputEl: function ()
34068     {
34069         return this.el.select('.roo-date-split-field-group-value', true).first();
34070     },
34071     
34072     onRender : function(ct, position) 
34073     {
34074         var _this = this;
34075         
34076         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34077         
34078         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34079         
34080         this.dayField = new Roo.bootstrap.ComboBox({
34081             allowBlank : this.dayAllowBlank,
34082             alwaysQuery : true,
34083             displayField : 'value',
34084             editable : false,
34085             fieldLabel : '',
34086             forceSelection : true,
34087             mode : 'local',
34088             placeholder : this.dayPlaceholder,
34089             selectOnFocus : true,
34090             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34091             triggerAction : 'all',
34092             typeAhead : true,
34093             valueField : 'value',
34094             store : new Roo.data.SimpleStore({
34095                 data : (function() {    
34096                     var days = [];
34097                     _this.fireEvent('days', _this, days);
34098                     return days;
34099                 })(),
34100                 fields : [ 'value' ]
34101             }),
34102             listeners : {
34103                 select : function (_self, record, index)
34104                 {
34105                     _this.setValue(_this.getValue());
34106                 }
34107             }
34108         });
34109
34110         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34111         
34112         this.monthField = new Roo.bootstrap.MonthField({
34113             after : '<i class=\"fa fa-calendar\"></i>',
34114             allowBlank : this.monthAllowBlank,
34115             placeholder : this.monthPlaceholder,
34116             readOnly : true,
34117             listeners : {
34118                 render : function (_self)
34119                 {
34120                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34121                         e.preventDefault();
34122                         _self.focus();
34123                     });
34124                 },
34125                 select : function (_self, oldvalue, newvalue)
34126                 {
34127                     _this.setValue(_this.getValue());
34128                 }
34129             }
34130         });
34131         
34132         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34133         
34134         this.yearField = new Roo.bootstrap.ComboBox({
34135             allowBlank : this.yearAllowBlank,
34136             alwaysQuery : true,
34137             displayField : 'value',
34138             editable : false,
34139             fieldLabel : '',
34140             forceSelection : true,
34141             mode : 'local',
34142             placeholder : this.yearPlaceholder,
34143             selectOnFocus : true,
34144             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34145             triggerAction : 'all',
34146             typeAhead : true,
34147             valueField : 'value',
34148             store : new Roo.data.SimpleStore({
34149                 data : (function() {
34150                     var years = [];
34151                     _this.fireEvent('years', _this, years);
34152                     return years;
34153                 })(),
34154                 fields : [ 'value' ]
34155             }),
34156             listeners : {
34157                 select : function (_self, record, index)
34158                 {
34159                     _this.setValue(_this.getValue());
34160                 }
34161             }
34162         });
34163
34164         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34165     },
34166     
34167     setValue : function(v, format)
34168     {
34169         this.inputEl.dom.value = v;
34170         
34171         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34172         
34173         var d = Date.parseDate(v, f);
34174         
34175         if(!d){
34176             this.validate();
34177             return;
34178         }
34179         
34180         this.setDay(d.format(this.dayFormat));
34181         this.setMonth(d.format(this.monthFormat));
34182         this.setYear(d.format(this.yearFormat));
34183         
34184         this.validate();
34185         
34186         return;
34187     },
34188     
34189     setDay : function(v)
34190     {
34191         this.dayField.setValue(v);
34192         this.inputEl.dom.value = this.getValue();
34193         this.validate();
34194         return;
34195     },
34196     
34197     setMonth : function(v)
34198     {
34199         this.monthField.setValue(v, true);
34200         this.inputEl.dom.value = this.getValue();
34201         this.validate();
34202         return;
34203     },
34204     
34205     setYear : function(v)
34206     {
34207         this.yearField.setValue(v);
34208         this.inputEl.dom.value = this.getValue();
34209         this.validate();
34210         return;
34211     },
34212     
34213     getDay : function()
34214     {
34215         return this.dayField.getValue();
34216     },
34217     
34218     getMonth : function()
34219     {
34220         return this.monthField.getValue();
34221     },
34222     
34223     getYear : function()
34224     {
34225         return this.yearField.getValue();
34226     },
34227     
34228     getValue : function()
34229     {
34230         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34231         
34232         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34233         
34234         return date;
34235     },
34236     
34237     reset : function()
34238     {
34239         this.setDay('');
34240         this.setMonth('');
34241         this.setYear('');
34242         this.inputEl.dom.value = '';
34243         this.validate();
34244         return;
34245     },
34246     
34247     validate : function()
34248     {
34249         var d = this.dayField.validate();
34250         var m = this.monthField.validate();
34251         var y = this.yearField.validate();
34252         
34253         var valid = true;
34254         
34255         if(
34256                 (!this.dayAllowBlank && !d) ||
34257                 (!this.monthAllowBlank && !m) ||
34258                 (!this.yearAllowBlank && !y)
34259         ){
34260             valid = false;
34261         }
34262         
34263         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34264             return valid;
34265         }
34266         
34267         if(valid){
34268             this.markValid();
34269             return valid;
34270         }
34271         
34272         this.markInvalid();
34273         
34274         return valid;
34275     },
34276     
34277     markValid : function()
34278     {
34279         
34280         var label = this.el.select('label', true).first();
34281         var icon = this.el.select('i.fa-star', true).first();
34282
34283         if(label && icon){
34284             icon.remove();
34285         }
34286         
34287         this.fireEvent('valid', this);
34288     },
34289     
34290      /**
34291      * Mark this field as invalid
34292      * @param {String} msg The validation message
34293      */
34294     markInvalid : function(msg)
34295     {
34296         
34297         var label = this.el.select('label', true).first();
34298         var icon = this.el.select('i.fa-star', true).first();
34299
34300         if(label && !icon){
34301             this.el.select('.roo-date-split-field-label', true).createChild({
34302                 tag : 'i',
34303                 cls : 'text-danger fa fa-lg fa-star',
34304                 tooltip : 'This field is required',
34305                 style : 'margin-right:5px;'
34306             }, label, true);
34307         }
34308         
34309         this.fireEvent('invalid', this, msg);
34310     },
34311     
34312     clearInvalid : function()
34313     {
34314         var label = this.el.select('label', true).first();
34315         var icon = this.el.select('i.fa-star', true).first();
34316
34317         if(label && icon){
34318             icon.remove();
34319         }
34320         
34321         this.fireEvent('valid', this);
34322     },
34323     
34324     getName: function()
34325     {
34326         return this.name;
34327     }
34328     
34329 });
34330
34331  /**
34332  *
34333  * This is based on 
34334  * http://masonry.desandro.com
34335  *
34336  * The idea is to render all the bricks based on vertical width...
34337  *
34338  * The original code extends 'outlayer' - we might need to use that....
34339  * 
34340  */
34341
34342
34343 /**
34344  * @class Roo.bootstrap.LayoutMasonry
34345  * @extends Roo.bootstrap.Component
34346  * Bootstrap Layout Masonry class
34347  * 
34348  * @constructor
34349  * Create a new Element
34350  * @param {Object} config The config object
34351  */
34352
34353 Roo.bootstrap.LayoutMasonry = function(config){
34354     
34355     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34356     
34357     this.bricks = [];
34358     
34359     Roo.bootstrap.LayoutMasonry.register(this);
34360     
34361     this.addEvents({
34362         // raw events
34363         /**
34364          * @event layout
34365          * Fire after layout the items
34366          * @param {Roo.bootstrap.LayoutMasonry} this
34367          * @param {Roo.EventObject} e
34368          */
34369         "layout" : true
34370     });
34371     
34372 };
34373
34374 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34375     
34376     /**
34377      * @cfg {Boolean} isLayoutInstant = no animation?
34378      */   
34379     isLayoutInstant : false, // needed?
34380    
34381     /**
34382      * @cfg {Number} boxWidth  width of the columns
34383      */   
34384     boxWidth : 450,
34385     
34386       /**
34387      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34388      */   
34389     boxHeight : 0,
34390     
34391     /**
34392      * @cfg {Number} padWidth padding below box..
34393      */   
34394     padWidth : 10, 
34395     
34396     /**
34397      * @cfg {Number} gutter gutter width..
34398      */   
34399     gutter : 10,
34400     
34401      /**
34402      * @cfg {Number} maxCols maximum number of columns
34403      */   
34404     
34405     maxCols: 0,
34406     
34407     /**
34408      * @cfg {Boolean} isAutoInitial defalut true
34409      */   
34410     isAutoInitial : true, 
34411     
34412     containerWidth: 0,
34413     
34414     /**
34415      * @cfg {Boolean} isHorizontal defalut false
34416      */   
34417     isHorizontal : false, 
34418
34419     currentSize : null,
34420     
34421     tag: 'div',
34422     
34423     cls: '',
34424     
34425     bricks: null, //CompositeElement
34426     
34427     cols : 1,
34428     
34429     _isLayoutInited : false,
34430     
34431 //    isAlternative : false, // only use for vertical layout...
34432     
34433     /**
34434      * @cfg {Number} alternativePadWidth padding below box..
34435      */   
34436     alternativePadWidth : 50,
34437     
34438     selectedBrick : [],
34439     
34440     getAutoCreate : function(){
34441         
34442         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34443         
34444         var cfg = {
34445             tag: this.tag,
34446             cls: 'blog-masonary-wrapper ' + this.cls,
34447             cn : {
34448                 cls : 'mas-boxes masonary'
34449             }
34450         };
34451         
34452         return cfg;
34453     },
34454     
34455     getChildContainer: function( )
34456     {
34457         if (this.boxesEl) {
34458             return this.boxesEl;
34459         }
34460         
34461         this.boxesEl = this.el.select('.mas-boxes').first();
34462         
34463         return this.boxesEl;
34464     },
34465     
34466     
34467     initEvents : function()
34468     {
34469         var _this = this;
34470         
34471         if(this.isAutoInitial){
34472             Roo.log('hook children rendered');
34473             this.on('childrenrendered', function() {
34474                 Roo.log('children rendered');
34475                 _this.initial();
34476             } ,this);
34477         }
34478     },
34479     
34480     initial : function()
34481     {
34482         this.selectedBrick = [];
34483         
34484         this.currentSize = this.el.getBox(true);
34485         
34486         Roo.EventManager.onWindowResize(this.resize, this); 
34487
34488         if(!this.isAutoInitial){
34489             this.layout();
34490             return;
34491         }
34492         
34493         this.layout();
34494         
34495         return;
34496         //this.layout.defer(500,this);
34497         
34498     },
34499     
34500     resize : function()
34501     {
34502         var cs = this.el.getBox(true);
34503         
34504         if (
34505                 this.currentSize.width == cs.width && 
34506                 this.currentSize.x == cs.x && 
34507                 this.currentSize.height == cs.height && 
34508                 this.currentSize.y == cs.y 
34509         ) {
34510             Roo.log("no change in with or X or Y");
34511             return;
34512         }
34513         
34514         this.currentSize = cs;
34515         
34516         this.layout();
34517         
34518     },
34519     
34520     layout : function()
34521     {   
34522         this._resetLayout();
34523         
34524         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34525         
34526         this.layoutItems( isInstant );
34527       
34528         this._isLayoutInited = true;
34529         
34530         this.fireEvent('layout', this);
34531         
34532     },
34533     
34534     _resetLayout : function()
34535     {
34536         if(this.isHorizontal){
34537             this.horizontalMeasureColumns();
34538             return;
34539         }
34540         
34541         this.verticalMeasureColumns();
34542         
34543     },
34544     
34545     verticalMeasureColumns : function()
34546     {
34547         this.getContainerWidth();
34548         
34549 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34550 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34551 //            return;
34552 //        }
34553         
34554         var boxWidth = this.boxWidth + this.padWidth;
34555         
34556         if(this.containerWidth < this.boxWidth){
34557             boxWidth = this.containerWidth
34558         }
34559         
34560         var containerWidth = this.containerWidth;
34561         
34562         var cols = Math.floor(containerWidth / boxWidth);
34563         
34564         this.cols = Math.max( cols, 1 );
34565         
34566         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34567         
34568         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34569         
34570         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34571         
34572         this.colWidth = boxWidth + avail - this.padWidth;
34573         
34574         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34575         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34576     },
34577     
34578     horizontalMeasureColumns : function()
34579     {
34580         this.getContainerWidth();
34581         
34582         var boxWidth = this.boxWidth;
34583         
34584         if(this.containerWidth < boxWidth){
34585             boxWidth = this.containerWidth;
34586         }
34587         
34588         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34589         
34590         this.el.setHeight(boxWidth);
34591         
34592     },
34593     
34594     getContainerWidth : function()
34595     {
34596         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34597     },
34598     
34599     layoutItems : function( isInstant )
34600     {
34601         Roo.log(this.bricks);
34602         
34603         var items = Roo.apply([], this.bricks);
34604         
34605         if(this.isHorizontal){
34606             this._horizontalLayoutItems( items , isInstant );
34607             return;
34608         }
34609         
34610 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34611 //            this._verticalAlternativeLayoutItems( items , isInstant );
34612 //            return;
34613 //        }
34614         
34615         this._verticalLayoutItems( items , isInstant );
34616         
34617     },
34618     
34619     _verticalLayoutItems : function ( items , isInstant)
34620     {
34621         if ( !items || !items.length ) {
34622             return;
34623         }
34624         
34625         var standard = [
34626             ['xs', 'xs', 'xs', 'tall'],
34627             ['xs', 'xs', 'tall'],
34628             ['xs', 'xs', 'sm'],
34629             ['xs', 'xs', 'xs'],
34630             ['xs', 'tall'],
34631             ['xs', 'sm'],
34632             ['xs', 'xs'],
34633             ['xs'],
34634             
34635             ['sm', 'xs', 'xs'],
34636             ['sm', 'xs'],
34637             ['sm'],
34638             
34639             ['tall', 'xs', 'xs', 'xs'],
34640             ['tall', 'xs', 'xs'],
34641             ['tall', 'xs'],
34642             ['tall']
34643             
34644         ];
34645         
34646         var queue = [];
34647         
34648         var boxes = [];
34649         
34650         var box = [];
34651         
34652         Roo.each(items, function(item, k){
34653             
34654             switch (item.size) {
34655                 // these layouts take up a full box,
34656                 case 'md' :
34657                 case 'md-left' :
34658                 case 'md-right' :
34659                 case 'wide' :
34660                     
34661                     if(box.length){
34662                         boxes.push(box);
34663                         box = [];
34664                     }
34665                     
34666                     boxes.push([item]);
34667                     
34668                     break;
34669                     
34670                 case 'xs' :
34671                 case 'sm' :
34672                 case 'tall' :
34673                     
34674                     box.push(item);
34675                     
34676                     break;
34677                 default :
34678                     break;
34679                     
34680             }
34681             
34682         }, this);
34683         
34684         if(box.length){
34685             boxes.push(box);
34686             box = [];
34687         }
34688         
34689         var filterPattern = function(box, length)
34690         {
34691             if(!box.length){
34692                 return;
34693             }
34694             
34695             var match = false;
34696             
34697             var pattern = box.slice(0, length);
34698             
34699             var format = [];
34700             
34701             Roo.each(pattern, function(i){
34702                 format.push(i.size);
34703             }, this);
34704             
34705             Roo.each(standard, function(s){
34706                 
34707                 if(String(s) != String(format)){
34708                     return;
34709                 }
34710                 
34711                 match = true;
34712                 return false;
34713                 
34714             }, this);
34715             
34716             if(!match && length == 1){
34717                 return;
34718             }
34719             
34720             if(!match){
34721                 filterPattern(box, length - 1);
34722                 return;
34723             }
34724                 
34725             queue.push(pattern);
34726
34727             box = box.slice(length, box.length);
34728
34729             filterPattern(box, 4);
34730
34731             return;
34732             
34733         }
34734         
34735         Roo.each(boxes, function(box, k){
34736             
34737             if(!box.length){
34738                 return;
34739             }
34740             
34741             if(box.length == 1){
34742                 queue.push(box);
34743                 return;
34744             }
34745             
34746             filterPattern(box, 4);
34747             
34748         }, this);
34749         
34750         this._processVerticalLayoutQueue( queue, isInstant );
34751         
34752     },
34753     
34754 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34755 //    {
34756 //        if ( !items || !items.length ) {
34757 //            return;
34758 //        }
34759 //
34760 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34761 //        
34762 //    },
34763     
34764     _horizontalLayoutItems : function ( items , isInstant)
34765     {
34766         if ( !items || !items.length || items.length < 3) {
34767             return;
34768         }
34769         
34770         items.reverse();
34771         
34772         var eItems = items.slice(0, 3);
34773         
34774         items = items.slice(3, items.length);
34775         
34776         var standard = [
34777             ['xs', 'xs', 'xs', 'wide'],
34778             ['xs', 'xs', 'wide'],
34779             ['xs', 'xs', 'sm'],
34780             ['xs', 'xs', 'xs'],
34781             ['xs', 'wide'],
34782             ['xs', 'sm'],
34783             ['xs', 'xs'],
34784             ['xs'],
34785             
34786             ['sm', 'xs', 'xs'],
34787             ['sm', 'xs'],
34788             ['sm'],
34789             
34790             ['wide', 'xs', 'xs', 'xs'],
34791             ['wide', 'xs', 'xs'],
34792             ['wide', 'xs'],
34793             ['wide'],
34794             
34795             ['wide-thin']
34796         ];
34797         
34798         var queue = [];
34799         
34800         var boxes = [];
34801         
34802         var box = [];
34803         
34804         Roo.each(items, function(item, k){
34805             
34806             switch (item.size) {
34807                 case 'md' :
34808                 case 'md-left' :
34809                 case 'md-right' :
34810                 case 'tall' :
34811                     
34812                     if(box.length){
34813                         boxes.push(box);
34814                         box = [];
34815                     }
34816                     
34817                     boxes.push([item]);
34818                     
34819                     break;
34820                     
34821                 case 'xs' :
34822                 case 'sm' :
34823                 case 'wide' :
34824                 case 'wide-thin' :
34825                     
34826                     box.push(item);
34827                     
34828                     break;
34829                 default :
34830                     break;
34831                     
34832             }
34833             
34834         }, this);
34835         
34836         if(box.length){
34837             boxes.push(box);
34838             box = [];
34839         }
34840         
34841         var filterPattern = function(box, length)
34842         {
34843             if(!box.length){
34844                 return;
34845             }
34846             
34847             var match = false;
34848             
34849             var pattern = box.slice(0, length);
34850             
34851             var format = [];
34852             
34853             Roo.each(pattern, function(i){
34854                 format.push(i.size);
34855             }, this);
34856             
34857             Roo.each(standard, function(s){
34858                 
34859                 if(String(s) != String(format)){
34860                     return;
34861                 }
34862                 
34863                 match = true;
34864                 return false;
34865                 
34866             }, this);
34867             
34868             if(!match && length == 1){
34869                 return;
34870             }
34871             
34872             if(!match){
34873                 filterPattern(box, length - 1);
34874                 return;
34875             }
34876                 
34877             queue.push(pattern);
34878
34879             box = box.slice(length, box.length);
34880
34881             filterPattern(box, 4);
34882
34883             return;
34884             
34885         }
34886         
34887         Roo.each(boxes, function(box, k){
34888             
34889             if(!box.length){
34890                 return;
34891             }
34892             
34893             if(box.length == 1){
34894                 queue.push(box);
34895                 return;
34896             }
34897             
34898             filterPattern(box, 4);
34899             
34900         }, this);
34901         
34902         
34903         var prune = [];
34904         
34905         var pos = this.el.getBox(true);
34906         
34907         var minX = pos.x;
34908         
34909         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34910         
34911         var hit_end = false;
34912         
34913         Roo.each(queue, function(box){
34914             
34915             if(hit_end){
34916                 
34917                 Roo.each(box, function(b){
34918                 
34919                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34920                     b.el.hide();
34921
34922                 }, this);
34923
34924                 return;
34925             }
34926             
34927             var mx = 0;
34928             
34929             Roo.each(box, function(b){
34930                 
34931                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34932                 b.el.show();
34933
34934                 mx = Math.max(mx, b.x);
34935                 
34936             }, this);
34937             
34938             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34939             
34940             if(maxX < minX){
34941                 
34942                 Roo.each(box, function(b){
34943                 
34944                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34945                     b.el.hide();
34946                     
34947                 }, this);
34948                 
34949                 hit_end = true;
34950                 
34951                 return;
34952             }
34953             
34954             prune.push(box);
34955             
34956         }, this);
34957         
34958         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34959     },
34960     
34961     /** Sets position of item in DOM
34962     * @param {Element} item
34963     * @param {Number} x - horizontal position
34964     * @param {Number} y - vertical position
34965     * @param {Boolean} isInstant - disables transitions
34966     */
34967     _processVerticalLayoutQueue : function( queue, isInstant )
34968     {
34969         var pos = this.el.getBox(true);
34970         var x = pos.x;
34971         var y = pos.y;
34972         var maxY = [];
34973         
34974         for (var i = 0; i < this.cols; i++){
34975             maxY[i] = pos.y;
34976         }
34977         
34978         Roo.each(queue, function(box, k){
34979             
34980             var col = k % this.cols;
34981             
34982             Roo.each(box, function(b,kk){
34983                 
34984                 b.el.position('absolute');
34985                 
34986                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34987                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34988                 
34989                 if(b.size == 'md-left' || b.size == 'md-right'){
34990                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34991                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34992                 }
34993                 
34994                 b.el.setWidth(width);
34995                 b.el.setHeight(height);
34996                 // iframe?
34997                 b.el.select('iframe',true).setSize(width,height);
34998                 
34999             }, this);
35000             
35001             for (var i = 0; i < this.cols; i++){
35002                 
35003                 if(maxY[i] < maxY[col]){
35004                     col = i;
35005                     continue;
35006                 }
35007                 
35008                 col = Math.min(col, i);
35009                 
35010             }
35011             
35012             x = pos.x + col * (this.colWidth + this.padWidth);
35013             
35014             y = maxY[col];
35015             
35016             var positions = [];
35017             
35018             switch (box.length){
35019                 case 1 :
35020                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35021                     break;
35022                 case 2 :
35023                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35024                     break;
35025                 case 3 :
35026                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35027                     break;
35028                 case 4 :
35029                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35030                     break;
35031                 default :
35032                     break;
35033             }
35034             
35035             Roo.each(box, function(b,kk){
35036                 
35037                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35038                 
35039                 var sz = b.el.getSize();
35040                 
35041                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35042                 
35043             }, this);
35044             
35045         }, this);
35046         
35047         var mY = 0;
35048         
35049         for (var i = 0; i < this.cols; i++){
35050             mY = Math.max(mY, maxY[i]);
35051         }
35052         
35053         this.el.setHeight(mY - pos.y);
35054         
35055     },
35056     
35057 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35058 //    {
35059 //        var pos = this.el.getBox(true);
35060 //        var x = pos.x;
35061 //        var y = pos.y;
35062 //        var maxX = pos.right;
35063 //        
35064 //        var maxHeight = 0;
35065 //        
35066 //        Roo.each(items, function(item, k){
35067 //            
35068 //            var c = k % 2;
35069 //            
35070 //            item.el.position('absolute');
35071 //                
35072 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35073 //
35074 //            item.el.setWidth(width);
35075 //
35076 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35077 //
35078 //            item.el.setHeight(height);
35079 //            
35080 //            if(c == 0){
35081 //                item.el.setXY([x, y], isInstant ? false : true);
35082 //            } else {
35083 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35084 //            }
35085 //            
35086 //            y = y + height + this.alternativePadWidth;
35087 //            
35088 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35089 //            
35090 //        }, this);
35091 //        
35092 //        this.el.setHeight(maxHeight);
35093 //        
35094 //    },
35095     
35096     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35097     {
35098         var pos = this.el.getBox(true);
35099         
35100         var minX = pos.x;
35101         var minY = pos.y;
35102         
35103         var maxX = pos.right;
35104         
35105         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35106         
35107         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35108         
35109         Roo.each(queue, function(box, k){
35110             
35111             Roo.each(box, function(b, kk){
35112                 
35113                 b.el.position('absolute');
35114                 
35115                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35116                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35117                 
35118                 if(b.size == 'md-left' || b.size == 'md-right'){
35119                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35120                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35121                 }
35122                 
35123                 b.el.setWidth(width);
35124                 b.el.setHeight(height);
35125                 
35126             }, this);
35127             
35128             if(!box.length){
35129                 return;
35130             }
35131             
35132             var positions = [];
35133             
35134             switch (box.length){
35135                 case 1 :
35136                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35137                     break;
35138                 case 2 :
35139                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35140                     break;
35141                 case 3 :
35142                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35143                     break;
35144                 case 4 :
35145                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35146                     break;
35147                 default :
35148                     break;
35149             }
35150             
35151             Roo.each(box, function(b,kk){
35152                 
35153                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35154                 
35155                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35156                 
35157             }, this);
35158             
35159         }, this);
35160         
35161     },
35162     
35163     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35164     {
35165         Roo.each(eItems, function(b,k){
35166             
35167             b.size = (k == 0) ? 'sm' : 'xs';
35168             b.x = (k == 0) ? 2 : 1;
35169             b.y = (k == 0) ? 2 : 1;
35170             
35171             b.el.position('absolute');
35172             
35173             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35174                 
35175             b.el.setWidth(width);
35176             
35177             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35178             
35179             b.el.setHeight(height);
35180             
35181         }, this);
35182
35183         var positions = [];
35184         
35185         positions.push({
35186             x : maxX - this.unitWidth * 2 - this.gutter,
35187             y : minY
35188         });
35189         
35190         positions.push({
35191             x : maxX - this.unitWidth,
35192             y : minY + (this.unitWidth + this.gutter) * 2
35193         });
35194         
35195         positions.push({
35196             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35197             y : minY
35198         });
35199         
35200         Roo.each(eItems, function(b,k){
35201             
35202             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35203
35204         }, this);
35205         
35206     },
35207     
35208     getVerticalOneBoxColPositions : function(x, y, box)
35209     {
35210         var pos = [];
35211         
35212         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35213         
35214         if(box[0].size == 'md-left'){
35215             rand = 0;
35216         }
35217         
35218         if(box[0].size == 'md-right'){
35219             rand = 1;
35220         }
35221         
35222         pos.push({
35223             x : x + (this.unitWidth + this.gutter) * rand,
35224             y : y
35225         });
35226         
35227         return pos;
35228     },
35229     
35230     getVerticalTwoBoxColPositions : function(x, y, box)
35231     {
35232         var pos = [];
35233         
35234         if(box[0].size == 'xs'){
35235             
35236             pos.push({
35237                 x : x,
35238                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35239             });
35240
35241             pos.push({
35242                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35243                 y : y
35244             });
35245             
35246             return pos;
35247             
35248         }
35249         
35250         pos.push({
35251             x : x,
35252             y : y
35253         });
35254
35255         pos.push({
35256             x : x + (this.unitWidth + this.gutter) * 2,
35257             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35258         });
35259         
35260         return pos;
35261         
35262     },
35263     
35264     getVerticalThreeBoxColPositions : function(x, y, box)
35265     {
35266         var pos = [];
35267         
35268         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35269             
35270             pos.push({
35271                 x : x,
35272                 y : y
35273             });
35274
35275             pos.push({
35276                 x : x + (this.unitWidth + this.gutter) * 1,
35277                 y : y
35278             });
35279             
35280             pos.push({
35281                 x : x + (this.unitWidth + this.gutter) * 2,
35282                 y : y
35283             });
35284             
35285             return pos;
35286             
35287         }
35288         
35289         if(box[0].size == 'xs' && box[1].size == 'xs'){
35290             
35291             pos.push({
35292                 x : x,
35293                 y : y
35294             });
35295
35296             pos.push({
35297                 x : x,
35298                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35299             });
35300             
35301             pos.push({
35302                 x : x + (this.unitWidth + this.gutter) * 1,
35303                 y : y
35304             });
35305             
35306             return pos;
35307             
35308         }
35309         
35310         pos.push({
35311             x : x,
35312             y : y
35313         });
35314
35315         pos.push({
35316             x : x + (this.unitWidth + this.gutter) * 2,
35317             y : y
35318         });
35319
35320         pos.push({
35321             x : x + (this.unitWidth + this.gutter) * 2,
35322             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35323         });
35324             
35325         return pos;
35326         
35327     },
35328     
35329     getVerticalFourBoxColPositions : function(x, y, box)
35330     {
35331         var pos = [];
35332         
35333         if(box[0].size == 'xs'){
35334             
35335             pos.push({
35336                 x : x,
35337                 y : y
35338             });
35339
35340             pos.push({
35341                 x : x,
35342                 y : y + (this.unitHeight + this.gutter) * 1
35343             });
35344             
35345             pos.push({
35346                 x : x,
35347                 y : y + (this.unitHeight + this.gutter) * 2
35348             });
35349             
35350             pos.push({
35351                 x : x + (this.unitWidth + this.gutter) * 1,
35352                 y : y
35353             });
35354             
35355             return pos;
35356             
35357         }
35358         
35359         pos.push({
35360             x : x,
35361             y : y
35362         });
35363
35364         pos.push({
35365             x : x + (this.unitWidth + this.gutter) * 2,
35366             y : y
35367         });
35368
35369         pos.push({
35370             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35371             y : y + (this.unitHeight + this.gutter) * 1
35372         });
35373
35374         pos.push({
35375             x : x + (this.unitWidth + this.gutter) * 2,
35376             y : y + (this.unitWidth + this.gutter) * 2
35377         });
35378
35379         return pos;
35380         
35381     },
35382     
35383     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35384     {
35385         var pos = [];
35386         
35387         if(box[0].size == 'md-left'){
35388             pos.push({
35389                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35390                 y : minY
35391             });
35392             
35393             return pos;
35394         }
35395         
35396         if(box[0].size == 'md-right'){
35397             pos.push({
35398                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35399                 y : minY + (this.unitWidth + this.gutter) * 1
35400             });
35401             
35402             return pos;
35403         }
35404         
35405         var rand = Math.floor(Math.random() * (4 - box[0].y));
35406         
35407         pos.push({
35408             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35409             y : minY + (this.unitWidth + this.gutter) * rand
35410         });
35411         
35412         return pos;
35413         
35414     },
35415     
35416     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35417     {
35418         var pos = [];
35419         
35420         if(box[0].size == 'xs'){
35421             
35422             pos.push({
35423                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35424                 y : minY
35425             });
35426
35427             pos.push({
35428                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35429                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35430             });
35431             
35432             return pos;
35433             
35434         }
35435         
35436         pos.push({
35437             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35438             y : minY
35439         });
35440
35441         pos.push({
35442             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35443             y : minY + (this.unitWidth + this.gutter) * 2
35444         });
35445         
35446         return pos;
35447         
35448     },
35449     
35450     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35451     {
35452         var pos = [];
35453         
35454         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35455             
35456             pos.push({
35457                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35458                 y : minY
35459             });
35460
35461             pos.push({
35462                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35463                 y : minY + (this.unitWidth + this.gutter) * 1
35464             });
35465             
35466             pos.push({
35467                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35468                 y : minY + (this.unitWidth + this.gutter) * 2
35469             });
35470             
35471             return pos;
35472             
35473         }
35474         
35475         if(box[0].size == 'xs' && box[1].size == 'xs'){
35476             
35477             pos.push({
35478                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35479                 y : minY
35480             });
35481
35482             pos.push({
35483                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35484                 y : minY
35485             });
35486             
35487             pos.push({
35488                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35489                 y : minY + (this.unitWidth + this.gutter) * 1
35490             });
35491             
35492             return pos;
35493             
35494         }
35495         
35496         pos.push({
35497             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35498             y : minY
35499         });
35500
35501         pos.push({
35502             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35503             y : minY + (this.unitWidth + this.gutter) * 2
35504         });
35505
35506         pos.push({
35507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35508             y : minY + (this.unitWidth + this.gutter) * 2
35509         });
35510             
35511         return pos;
35512         
35513     },
35514     
35515     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35516     {
35517         var pos = [];
35518         
35519         if(box[0].size == 'xs'){
35520             
35521             pos.push({
35522                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35523                 y : minY
35524             });
35525
35526             pos.push({
35527                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35528                 y : minY
35529             });
35530             
35531             pos.push({
35532                 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),
35533                 y : minY
35534             });
35535             
35536             pos.push({
35537                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35538                 y : minY + (this.unitWidth + this.gutter) * 1
35539             });
35540             
35541             return pos;
35542             
35543         }
35544         
35545         pos.push({
35546             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35547             y : minY
35548         });
35549         
35550         pos.push({
35551             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35552             y : minY + (this.unitWidth + this.gutter) * 2
35553         });
35554         
35555         pos.push({
35556             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35557             y : minY + (this.unitWidth + this.gutter) * 2
35558         });
35559         
35560         pos.push({
35561             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),
35562             y : minY + (this.unitWidth + this.gutter) * 2
35563         });
35564
35565         return pos;
35566         
35567     },
35568     
35569     /**
35570     * remove a Masonry Brick
35571     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35572     */
35573     removeBrick : function(brick_id)
35574     {
35575         if (!brick_id) {
35576             return;
35577         }
35578         
35579         for (var i = 0; i<this.bricks.length; i++) {
35580             if (this.bricks[i].id == brick_id) {
35581                 this.bricks.splice(i,1);
35582                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35583                 this.initial();
35584             }
35585         }
35586     },
35587     
35588     /**
35589     * adds a Masonry Brick
35590     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35591     */
35592     addBrick : function(cfg)
35593     {
35594         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35595         //this.register(cn);
35596         cn.parentId = this.id;
35597         cn.render(this.el);
35598         return cn;
35599     },
35600     
35601     /**
35602     * register a Masonry Brick
35603     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35604     */
35605     
35606     register : function(brick)
35607     {
35608         this.bricks.push(brick);
35609         brick.masonryId = this.id;
35610     },
35611     
35612     /**
35613     * clear all the Masonry Brick
35614     */
35615     clearAll : function()
35616     {
35617         this.bricks = [];
35618         //this.getChildContainer().dom.innerHTML = "";
35619         this.el.dom.innerHTML = '';
35620     },
35621     
35622     getSelected : function()
35623     {
35624         if (!this.selectedBrick) {
35625             return false;
35626         }
35627         
35628         return this.selectedBrick;
35629     }
35630 });
35631
35632 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35633     
35634     groups: {},
35635      /**
35636     * register a Masonry Layout
35637     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35638     */
35639     
35640     register : function(layout)
35641     {
35642         this.groups[layout.id] = layout;
35643     },
35644     /**
35645     * fetch a  Masonry Layout based on the masonry layout ID
35646     * @param {string} the masonry layout to add
35647     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35648     */
35649     
35650     get: function(layout_id) {
35651         if (typeof(this.groups[layout_id]) == 'undefined') {
35652             return false;
35653         }
35654         return this.groups[layout_id] ;
35655     }
35656     
35657     
35658     
35659 });
35660
35661  
35662
35663  /**
35664  *
35665  * This is based on 
35666  * http://masonry.desandro.com
35667  *
35668  * The idea is to render all the bricks based on vertical width...
35669  *
35670  * The original code extends 'outlayer' - we might need to use that....
35671  * 
35672  */
35673
35674
35675 /**
35676  * @class Roo.bootstrap.LayoutMasonryAuto
35677  * @extends Roo.bootstrap.Component
35678  * Bootstrap Layout Masonry class
35679  * 
35680  * @constructor
35681  * Create a new Element
35682  * @param {Object} config The config object
35683  */
35684
35685 Roo.bootstrap.LayoutMasonryAuto = function(config){
35686     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35687 };
35688
35689 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35690     
35691       /**
35692      * @cfg {Boolean} isFitWidth  - resize the width..
35693      */   
35694     isFitWidth : false,  // options..
35695     /**
35696      * @cfg {Boolean} isOriginLeft = left align?
35697      */   
35698     isOriginLeft : true,
35699     /**
35700      * @cfg {Boolean} isOriginTop = top align?
35701      */   
35702     isOriginTop : false,
35703     /**
35704      * @cfg {Boolean} isLayoutInstant = no animation?
35705      */   
35706     isLayoutInstant : false, // needed?
35707     /**
35708      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35709      */   
35710     isResizingContainer : true,
35711     /**
35712      * @cfg {Number} columnWidth  width of the columns 
35713      */   
35714     
35715     columnWidth : 0,
35716     
35717     /**
35718      * @cfg {Number} maxCols maximum number of columns
35719      */   
35720     
35721     maxCols: 0,
35722     /**
35723      * @cfg {Number} padHeight padding below box..
35724      */   
35725     
35726     padHeight : 10, 
35727     
35728     /**
35729      * @cfg {Boolean} isAutoInitial defalut true
35730      */   
35731     
35732     isAutoInitial : true, 
35733     
35734     // private?
35735     gutter : 0,
35736     
35737     containerWidth: 0,
35738     initialColumnWidth : 0,
35739     currentSize : null,
35740     
35741     colYs : null, // array.
35742     maxY : 0,
35743     padWidth: 10,
35744     
35745     
35746     tag: 'div',
35747     cls: '',
35748     bricks: null, //CompositeElement
35749     cols : 0, // array?
35750     // element : null, // wrapped now this.el
35751     _isLayoutInited : null, 
35752     
35753     
35754     getAutoCreate : function(){
35755         
35756         var cfg = {
35757             tag: this.tag,
35758             cls: 'blog-masonary-wrapper ' + this.cls,
35759             cn : {
35760                 cls : 'mas-boxes masonary'
35761             }
35762         };
35763         
35764         return cfg;
35765     },
35766     
35767     getChildContainer: function( )
35768     {
35769         if (this.boxesEl) {
35770             return this.boxesEl;
35771         }
35772         
35773         this.boxesEl = this.el.select('.mas-boxes').first();
35774         
35775         return this.boxesEl;
35776     },
35777     
35778     
35779     initEvents : function()
35780     {
35781         var _this = this;
35782         
35783         if(this.isAutoInitial){
35784             Roo.log('hook children rendered');
35785             this.on('childrenrendered', function() {
35786                 Roo.log('children rendered');
35787                 _this.initial();
35788             } ,this);
35789         }
35790         
35791     },
35792     
35793     initial : function()
35794     {
35795         this.reloadItems();
35796
35797         this.currentSize = this.el.getBox(true);
35798
35799         /// was window resize... - let's see if this works..
35800         Roo.EventManager.onWindowResize(this.resize, this); 
35801
35802         if(!this.isAutoInitial){
35803             this.layout();
35804             return;
35805         }
35806         
35807         this.layout.defer(500,this);
35808     },
35809     
35810     reloadItems: function()
35811     {
35812         this.bricks = this.el.select('.masonry-brick', true);
35813         
35814         this.bricks.each(function(b) {
35815             //Roo.log(b.getSize());
35816             if (!b.attr('originalwidth')) {
35817                 b.attr('originalwidth',  b.getSize().width);
35818             }
35819             
35820         });
35821         
35822         Roo.log(this.bricks.elements.length);
35823     },
35824     
35825     resize : function()
35826     {
35827         Roo.log('resize');
35828         var cs = this.el.getBox(true);
35829         
35830         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35831             Roo.log("no change in with or X");
35832             return;
35833         }
35834         this.currentSize = cs;
35835         this.layout();
35836     },
35837     
35838     layout : function()
35839     {
35840          Roo.log('layout');
35841         this._resetLayout();
35842         //this._manageStamps();
35843       
35844         // don't animate first layout
35845         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35846         this.layoutItems( isInstant );
35847       
35848         // flag for initalized
35849         this._isLayoutInited = true;
35850     },
35851     
35852     layoutItems : function( isInstant )
35853     {
35854         //var items = this._getItemsForLayout( this.items );
35855         // original code supports filtering layout items.. we just ignore it..
35856         
35857         this._layoutItems( this.bricks , isInstant );
35858       
35859         this._postLayout();
35860     },
35861     _layoutItems : function ( items , isInstant)
35862     {
35863        //this.fireEvent( 'layout', this, items );
35864     
35865
35866         if ( !items || !items.elements.length ) {
35867           // no items, emit event with empty array
35868             return;
35869         }
35870
35871         var queue = [];
35872         items.each(function(item) {
35873             Roo.log("layout item");
35874             Roo.log(item);
35875             // get x/y object from method
35876             var position = this._getItemLayoutPosition( item );
35877             // enqueue
35878             position.item = item;
35879             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35880             queue.push( position );
35881         }, this);
35882       
35883         this._processLayoutQueue( queue );
35884     },
35885     /** Sets position of item in DOM
35886     * @param {Element} item
35887     * @param {Number} x - horizontal position
35888     * @param {Number} y - vertical position
35889     * @param {Boolean} isInstant - disables transitions
35890     */
35891     _processLayoutQueue : function( queue )
35892     {
35893         for ( var i=0, len = queue.length; i < len; i++ ) {
35894             var obj = queue[i];
35895             obj.item.position('absolute');
35896             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35897         }
35898     },
35899       
35900     
35901     /**
35902     * Any logic you want to do after each layout,
35903     * i.e. size the container
35904     */
35905     _postLayout : function()
35906     {
35907         this.resizeContainer();
35908     },
35909     
35910     resizeContainer : function()
35911     {
35912         if ( !this.isResizingContainer ) {
35913             return;
35914         }
35915         var size = this._getContainerSize();
35916         if ( size ) {
35917             this.el.setSize(size.width,size.height);
35918             this.boxesEl.setSize(size.width,size.height);
35919         }
35920     },
35921     
35922     
35923     
35924     _resetLayout : function()
35925     {
35926         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35927         this.colWidth = this.el.getWidth();
35928         //this.gutter = this.el.getWidth(); 
35929         
35930         this.measureColumns();
35931
35932         // reset column Y
35933         var i = this.cols;
35934         this.colYs = [];
35935         while (i--) {
35936             this.colYs.push( 0 );
35937         }
35938     
35939         this.maxY = 0;
35940     },
35941
35942     measureColumns : function()
35943     {
35944         this.getContainerWidth();
35945       // if columnWidth is 0, default to outerWidth of first item
35946         if ( !this.columnWidth ) {
35947             var firstItem = this.bricks.first();
35948             Roo.log(firstItem);
35949             this.columnWidth  = this.containerWidth;
35950             if (firstItem && firstItem.attr('originalwidth') ) {
35951                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35952             }
35953             // columnWidth fall back to item of first element
35954             Roo.log("set column width?");
35955                         this.initialColumnWidth = this.columnWidth  ;
35956
35957             // if first elem has no width, default to size of container
35958             
35959         }
35960         
35961         
35962         if (this.initialColumnWidth) {
35963             this.columnWidth = this.initialColumnWidth;
35964         }
35965         
35966         
35967             
35968         // column width is fixed at the top - however if container width get's smaller we should
35969         // reduce it...
35970         
35971         // this bit calcs how man columns..
35972             
35973         var columnWidth = this.columnWidth += this.gutter;
35974       
35975         // calculate columns
35976         var containerWidth = this.containerWidth + this.gutter;
35977         
35978         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35979         // fix rounding errors, typically with gutters
35980         var excess = columnWidth - containerWidth % columnWidth;
35981         
35982         
35983         // if overshoot is less than a pixel, round up, otherwise floor it
35984         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35985         cols = Math[ mathMethod ]( cols );
35986         this.cols = Math.max( cols, 1 );
35987         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35988         
35989          // padding positioning..
35990         var totalColWidth = this.cols * this.columnWidth;
35991         var padavail = this.containerWidth - totalColWidth;
35992         // so for 2 columns - we need 3 'pads'
35993         
35994         var padNeeded = (1+this.cols) * this.padWidth;
35995         
35996         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35997         
35998         this.columnWidth += padExtra
35999         //this.padWidth = Math.floor(padavail /  ( this.cols));
36000         
36001         // adjust colum width so that padding is fixed??
36002         
36003         // we have 3 columns ... total = width * 3
36004         // we have X left over... that should be used by 
36005         
36006         //if (this.expandC) {
36007             
36008         //}
36009         
36010         
36011         
36012     },
36013     
36014     getContainerWidth : function()
36015     {
36016        /* // container is parent if fit width
36017         var container = this.isFitWidth ? this.element.parentNode : this.element;
36018         // check that this.size and size are there
36019         // IE8 triggers resize on body size change, so they might not be
36020         
36021         var size = getSize( container );  //FIXME
36022         this.containerWidth = size && size.innerWidth; //FIXME
36023         */
36024          
36025         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36026         
36027     },
36028     
36029     _getItemLayoutPosition : function( item )  // what is item?
36030     {
36031         // we resize the item to our columnWidth..
36032       
36033         item.setWidth(this.columnWidth);
36034         item.autoBoxAdjust  = false;
36035         
36036         var sz = item.getSize();
36037  
36038         // how many columns does this brick span
36039         var remainder = this.containerWidth % this.columnWidth;
36040         
36041         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36042         // round if off by 1 pixel, otherwise use ceil
36043         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36044         colSpan = Math.min( colSpan, this.cols );
36045         
36046         // normally this should be '1' as we dont' currently allow multi width columns..
36047         
36048         var colGroup = this._getColGroup( colSpan );
36049         // get the minimum Y value from the columns
36050         var minimumY = Math.min.apply( Math, colGroup );
36051         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36052         
36053         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36054          
36055         // position the brick
36056         var position = {
36057             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36058             y: this.currentSize.y + minimumY + this.padHeight
36059         };
36060         
36061         Roo.log(position);
36062         // apply setHeight to necessary columns
36063         var setHeight = minimumY + sz.height + this.padHeight;
36064         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36065         
36066         var setSpan = this.cols + 1 - colGroup.length;
36067         for ( var i = 0; i < setSpan; i++ ) {
36068           this.colYs[ shortColIndex + i ] = setHeight ;
36069         }
36070       
36071         return position;
36072     },
36073     
36074     /**
36075      * @param {Number} colSpan - number of columns the element spans
36076      * @returns {Array} colGroup
36077      */
36078     _getColGroup : function( colSpan )
36079     {
36080         if ( colSpan < 2 ) {
36081           // if brick spans only one column, use all the column Ys
36082           return this.colYs;
36083         }
36084       
36085         var colGroup = [];
36086         // how many different places could this brick fit horizontally
36087         var groupCount = this.cols + 1 - colSpan;
36088         // for each group potential horizontal position
36089         for ( var i = 0; i < groupCount; i++ ) {
36090           // make an array of colY values for that one group
36091           var groupColYs = this.colYs.slice( i, i + colSpan );
36092           // and get the max value of the array
36093           colGroup[i] = Math.max.apply( Math, groupColYs );
36094         }
36095         return colGroup;
36096     },
36097     /*
36098     _manageStamp : function( stamp )
36099     {
36100         var stampSize =  stamp.getSize();
36101         var offset = stamp.getBox();
36102         // get the columns that this stamp affects
36103         var firstX = this.isOriginLeft ? offset.x : offset.right;
36104         var lastX = firstX + stampSize.width;
36105         var firstCol = Math.floor( firstX / this.columnWidth );
36106         firstCol = Math.max( 0, firstCol );
36107         
36108         var lastCol = Math.floor( lastX / this.columnWidth );
36109         // lastCol should not go over if multiple of columnWidth #425
36110         lastCol -= lastX % this.columnWidth ? 0 : 1;
36111         lastCol = Math.min( this.cols - 1, lastCol );
36112         
36113         // set colYs to bottom of the stamp
36114         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36115             stampSize.height;
36116             
36117         for ( var i = firstCol; i <= lastCol; i++ ) {
36118           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36119         }
36120     },
36121     */
36122     
36123     _getContainerSize : function()
36124     {
36125         this.maxY = Math.max.apply( Math, this.colYs );
36126         var size = {
36127             height: this.maxY
36128         };
36129       
36130         if ( this.isFitWidth ) {
36131             size.width = this._getContainerFitWidth();
36132         }
36133       
36134         return size;
36135     },
36136     
36137     _getContainerFitWidth : function()
36138     {
36139         var unusedCols = 0;
36140         // count unused columns
36141         var i = this.cols;
36142         while ( --i ) {
36143           if ( this.colYs[i] !== 0 ) {
36144             break;
36145           }
36146           unusedCols++;
36147         }
36148         // fit container to columns that have been used
36149         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36150     },
36151     
36152     needsResizeLayout : function()
36153     {
36154         var previousWidth = this.containerWidth;
36155         this.getContainerWidth();
36156         return previousWidth !== this.containerWidth;
36157     }
36158  
36159 });
36160
36161  
36162
36163  /*
36164  * - LGPL
36165  *
36166  * element
36167  * 
36168  */
36169
36170 /**
36171  * @class Roo.bootstrap.MasonryBrick
36172  * @extends Roo.bootstrap.Component
36173  * Bootstrap MasonryBrick class
36174  * 
36175  * @constructor
36176  * Create a new MasonryBrick
36177  * @param {Object} config The config object
36178  */
36179
36180 Roo.bootstrap.MasonryBrick = function(config){
36181     
36182     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36183     
36184     Roo.bootstrap.MasonryBrick.register(this);
36185     
36186     this.addEvents({
36187         // raw events
36188         /**
36189          * @event click
36190          * When a MasonryBrick is clcik
36191          * @param {Roo.bootstrap.MasonryBrick} this
36192          * @param {Roo.EventObject} e
36193          */
36194         "click" : true
36195     });
36196 };
36197
36198 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36199     
36200     /**
36201      * @cfg {String} title
36202      */   
36203     title : '',
36204     /**
36205      * @cfg {String} html
36206      */   
36207     html : '',
36208     /**
36209      * @cfg {String} bgimage
36210      */   
36211     bgimage : '',
36212     /**
36213      * @cfg {String} videourl
36214      */   
36215     videourl : '',
36216     /**
36217      * @cfg {String} cls
36218      */   
36219     cls : '',
36220     /**
36221      * @cfg {String} href
36222      */   
36223     href : '',
36224     /**
36225      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36226      */   
36227     size : 'xs',
36228     
36229     /**
36230      * @cfg {String} placetitle (center|bottom)
36231      */   
36232     placetitle : '',
36233     
36234     /**
36235      * @cfg {Boolean} isFitContainer defalut true
36236      */   
36237     isFitContainer : true, 
36238     
36239     /**
36240      * @cfg {Boolean} preventDefault defalut false
36241      */   
36242     preventDefault : false, 
36243     
36244     /**
36245      * @cfg {Boolean} inverse defalut false
36246      */   
36247     maskInverse : false, 
36248     
36249     getAutoCreate : function()
36250     {
36251         if(!this.isFitContainer){
36252             return this.getSplitAutoCreate();
36253         }
36254         
36255         var cls = 'masonry-brick masonry-brick-full';
36256         
36257         if(this.href.length){
36258             cls += ' masonry-brick-link';
36259         }
36260         
36261         if(this.bgimage.length){
36262             cls += ' masonry-brick-image';
36263         }
36264         
36265         if(this.maskInverse){
36266             cls += ' mask-inverse';
36267         }
36268         
36269         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36270             cls += ' enable-mask';
36271         }
36272         
36273         if(this.size){
36274             cls += ' masonry-' + this.size + '-brick';
36275         }
36276         
36277         if(this.placetitle.length){
36278             
36279             switch (this.placetitle) {
36280                 case 'center' :
36281                     cls += ' masonry-center-title';
36282                     break;
36283                 case 'bottom' :
36284                     cls += ' masonry-bottom-title';
36285                     break;
36286                 default:
36287                     break;
36288             }
36289             
36290         } else {
36291             if(!this.html.length && !this.bgimage.length){
36292                 cls += ' masonry-center-title';
36293             }
36294
36295             if(!this.html.length && this.bgimage.length){
36296                 cls += ' masonry-bottom-title';
36297             }
36298         }
36299         
36300         if(this.cls){
36301             cls += ' ' + this.cls;
36302         }
36303         
36304         var cfg = {
36305             tag: (this.href.length) ? 'a' : 'div',
36306             cls: cls,
36307             cn: [
36308                 {
36309                     tag: 'div',
36310                     cls: 'masonry-brick-mask'
36311                 },
36312                 {
36313                     tag: 'div',
36314                     cls: 'masonry-brick-paragraph',
36315                     cn: []
36316                 }
36317             ]
36318         };
36319         
36320         if(this.href.length){
36321             cfg.href = this.href;
36322         }
36323         
36324         var cn = cfg.cn[1].cn;
36325         
36326         if(this.title.length){
36327             cn.push({
36328                 tag: 'h4',
36329                 cls: 'masonry-brick-title',
36330                 html: this.title
36331             });
36332         }
36333         
36334         if(this.html.length){
36335             cn.push({
36336                 tag: 'p',
36337                 cls: 'masonry-brick-text',
36338                 html: this.html
36339             });
36340         }
36341         
36342         if (!this.title.length && !this.html.length) {
36343             cfg.cn[1].cls += ' hide';
36344         }
36345         
36346         if(this.bgimage.length){
36347             cfg.cn.push({
36348                 tag: 'img',
36349                 cls: 'masonry-brick-image-view',
36350                 src: this.bgimage
36351             });
36352         }
36353         
36354         if(this.videourl.length){
36355             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36356             // youtube support only?
36357             cfg.cn.push({
36358                 tag: 'iframe',
36359                 cls: 'masonry-brick-image-view',
36360                 src: vurl,
36361                 frameborder : 0,
36362                 allowfullscreen : true
36363             });
36364         }
36365         
36366         return cfg;
36367         
36368     },
36369     
36370     getSplitAutoCreate : function()
36371     {
36372         var cls = 'masonry-brick masonry-brick-split';
36373         
36374         if(this.href.length){
36375             cls += ' masonry-brick-link';
36376         }
36377         
36378         if(this.bgimage.length){
36379             cls += ' masonry-brick-image';
36380         }
36381         
36382         if(this.size){
36383             cls += ' masonry-' + this.size + '-brick';
36384         }
36385         
36386         switch (this.placetitle) {
36387             case 'center' :
36388                 cls += ' masonry-center-title';
36389                 break;
36390             case 'bottom' :
36391                 cls += ' masonry-bottom-title';
36392                 break;
36393             default:
36394                 if(!this.bgimage.length){
36395                     cls += ' masonry-center-title';
36396                 }
36397
36398                 if(this.bgimage.length){
36399                     cls += ' masonry-bottom-title';
36400                 }
36401                 break;
36402         }
36403         
36404         if(this.cls){
36405             cls += ' ' + this.cls;
36406         }
36407         
36408         var cfg = {
36409             tag: (this.href.length) ? 'a' : 'div',
36410             cls: cls,
36411             cn: [
36412                 {
36413                     tag: 'div',
36414                     cls: 'masonry-brick-split-head',
36415                     cn: [
36416                         {
36417                             tag: 'div',
36418                             cls: 'masonry-brick-paragraph',
36419                             cn: []
36420                         }
36421                     ]
36422                 },
36423                 {
36424                     tag: 'div',
36425                     cls: 'masonry-brick-split-body',
36426                     cn: []
36427                 }
36428             ]
36429         };
36430         
36431         if(this.href.length){
36432             cfg.href = this.href;
36433         }
36434         
36435         if(this.title.length){
36436             cfg.cn[0].cn[0].cn.push({
36437                 tag: 'h4',
36438                 cls: 'masonry-brick-title',
36439                 html: this.title
36440             });
36441         }
36442         
36443         if(this.html.length){
36444             cfg.cn[1].cn.push({
36445                 tag: 'p',
36446                 cls: 'masonry-brick-text',
36447                 html: this.html
36448             });
36449         }
36450
36451         if(this.bgimage.length){
36452             cfg.cn[0].cn.push({
36453                 tag: 'img',
36454                 cls: 'masonry-brick-image-view',
36455                 src: this.bgimage
36456             });
36457         }
36458         
36459         if(this.videourl.length){
36460             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36461             // youtube support only?
36462             cfg.cn[0].cn.cn.push({
36463                 tag: 'iframe',
36464                 cls: 'masonry-brick-image-view',
36465                 src: vurl,
36466                 frameborder : 0,
36467                 allowfullscreen : true
36468             });
36469         }
36470         
36471         return cfg;
36472     },
36473     
36474     initEvents: function() 
36475     {
36476         switch (this.size) {
36477             case 'xs' :
36478                 this.x = 1;
36479                 this.y = 1;
36480                 break;
36481             case 'sm' :
36482                 this.x = 2;
36483                 this.y = 2;
36484                 break;
36485             case 'md' :
36486             case 'md-left' :
36487             case 'md-right' :
36488                 this.x = 3;
36489                 this.y = 3;
36490                 break;
36491             case 'tall' :
36492                 this.x = 2;
36493                 this.y = 3;
36494                 break;
36495             case 'wide' :
36496                 this.x = 3;
36497                 this.y = 2;
36498                 break;
36499             case 'wide-thin' :
36500                 this.x = 3;
36501                 this.y = 1;
36502                 break;
36503                         
36504             default :
36505                 break;
36506         }
36507         
36508         if(Roo.isTouch){
36509             this.el.on('touchstart', this.onTouchStart, this);
36510             this.el.on('touchmove', this.onTouchMove, this);
36511             this.el.on('touchend', this.onTouchEnd, this);
36512             this.el.on('contextmenu', this.onContextMenu, this);
36513         } else {
36514             this.el.on('mouseenter'  ,this.enter, this);
36515             this.el.on('mouseleave', this.leave, this);
36516             this.el.on('click', this.onClick, this);
36517         }
36518         
36519         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36520             this.parent().bricks.push(this);   
36521         }
36522         
36523     },
36524     
36525     onClick: function(e, el)
36526     {
36527         var time = this.endTimer - this.startTimer;
36528         // Roo.log(e.preventDefault());
36529         if(Roo.isTouch){
36530             if(time > 1000){
36531                 e.preventDefault();
36532                 return;
36533             }
36534         }
36535         
36536         if(!this.preventDefault){
36537             return;
36538         }
36539         
36540         e.preventDefault();
36541         
36542         if (this.activeClass != '') {
36543             this.selectBrick();
36544         }
36545         
36546         this.fireEvent('click', this, e);
36547     },
36548     
36549     enter: function(e, el)
36550     {
36551         e.preventDefault();
36552         
36553         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36554             return;
36555         }
36556         
36557         if(this.bgimage.length && this.html.length){
36558             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36559         }
36560     },
36561     
36562     leave: function(e, el)
36563     {
36564         e.preventDefault();
36565         
36566         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36567             return;
36568         }
36569         
36570         if(this.bgimage.length && this.html.length){
36571             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36572         }
36573     },
36574     
36575     onTouchStart: function(e, el)
36576     {
36577 //        e.preventDefault();
36578         
36579         this.touchmoved = false;
36580         
36581         if(!this.isFitContainer){
36582             return;
36583         }
36584         
36585         if(!this.bgimage.length || !this.html.length){
36586             return;
36587         }
36588         
36589         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36590         
36591         this.timer = new Date().getTime();
36592         
36593     },
36594     
36595     onTouchMove: function(e, el)
36596     {
36597         this.touchmoved = true;
36598     },
36599     
36600     onContextMenu : function(e,el)
36601     {
36602         e.preventDefault();
36603         e.stopPropagation();
36604         return false;
36605     },
36606     
36607     onTouchEnd: function(e, el)
36608     {
36609 //        e.preventDefault();
36610         
36611         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36612         
36613             this.leave(e,el);
36614             
36615             return;
36616         }
36617         
36618         if(!this.bgimage.length || !this.html.length){
36619             
36620             if(this.href.length){
36621                 window.location.href = this.href;
36622             }
36623             
36624             return;
36625         }
36626         
36627         if(!this.isFitContainer){
36628             return;
36629         }
36630         
36631         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36632         
36633         window.location.href = this.href;
36634     },
36635     
36636     //selection on single brick only
36637     selectBrick : function() {
36638         
36639         if (!this.parentId) {
36640             return;
36641         }
36642         
36643         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36644         var index = m.selectedBrick.indexOf(this.id);
36645         
36646         if ( index > -1) {
36647             m.selectedBrick.splice(index,1);
36648             this.el.removeClass(this.activeClass);
36649             return;
36650         }
36651         
36652         for(var i = 0; i < m.selectedBrick.length; i++) {
36653             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36654             b.el.removeClass(b.activeClass);
36655         }
36656         
36657         m.selectedBrick = [];
36658         
36659         m.selectedBrick.push(this.id);
36660         this.el.addClass(this.activeClass);
36661         return;
36662     },
36663     
36664     isSelected : function(){
36665         return this.el.hasClass(this.activeClass);
36666         
36667     }
36668 });
36669
36670 Roo.apply(Roo.bootstrap.MasonryBrick, {
36671     
36672     //groups: {},
36673     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36674      /**
36675     * register a Masonry Brick
36676     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36677     */
36678     
36679     register : function(brick)
36680     {
36681         //this.groups[brick.id] = brick;
36682         this.groups.add(brick.id, brick);
36683     },
36684     /**
36685     * fetch a  masonry brick based on the masonry brick ID
36686     * @param {string} the masonry brick to add
36687     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36688     */
36689     
36690     get: function(brick_id) 
36691     {
36692         // if (typeof(this.groups[brick_id]) == 'undefined') {
36693         //     return false;
36694         // }
36695         // return this.groups[brick_id] ;
36696         
36697         if(this.groups.key(brick_id)) {
36698             return this.groups.key(brick_id);
36699         }
36700         
36701         return false;
36702     }
36703     
36704     
36705     
36706 });
36707
36708  /*
36709  * - LGPL
36710  *
36711  * element
36712  * 
36713  */
36714
36715 /**
36716  * @class Roo.bootstrap.Brick
36717  * @extends Roo.bootstrap.Component
36718  * Bootstrap Brick class
36719  * 
36720  * @constructor
36721  * Create a new Brick
36722  * @param {Object} config The config object
36723  */
36724
36725 Roo.bootstrap.Brick = function(config){
36726     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36727     
36728     this.addEvents({
36729         // raw events
36730         /**
36731          * @event click
36732          * When a Brick is click
36733          * @param {Roo.bootstrap.Brick} this
36734          * @param {Roo.EventObject} e
36735          */
36736         "click" : true
36737     });
36738 };
36739
36740 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36741     
36742     /**
36743      * @cfg {String} title
36744      */   
36745     title : '',
36746     /**
36747      * @cfg {String} html
36748      */   
36749     html : '',
36750     /**
36751      * @cfg {String} bgimage
36752      */   
36753     bgimage : '',
36754     /**
36755      * @cfg {String} cls
36756      */   
36757     cls : '',
36758     /**
36759      * @cfg {String} href
36760      */   
36761     href : '',
36762     /**
36763      * @cfg {String} video
36764      */   
36765     video : '',
36766     /**
36767      * @cfg {Boolean} square
36768      */   
36769     square : true,
36770     
36771     getAutoCreate : function()
36772     {
36773         var cls = 'roo-brick';
36774         
36775         if(this.href.length){
36776             cls += ' roo-brick-link';
36777         }
36778         
36779         if(this.bgimage.length){
36780             cls += ' roo-brick-image';
36781         }
36782         
36783         if(!this.html.length && !this.bgimage.length){
36784             cls += ' roo-brick-center-title';
36785         }
36786         
36787         if(!this.html.length && this.bgimage.length){
36788             cls += ' roo-brick-bottom-title';
36789         }
36790         
36791         if(this.cls){
36792             cls += ' ' + this.cls;
36793         }
36794         
36795         var cfg = {
36796             tag: (this.href.length) ? 'a' : 'div',
36797             cls: cls,
36798             cn: [
36799                 {
36800                     tag: 'div',
36801                     cls: 'roo-brick-paragraph',
36802                     cn: []
36803                 }
36804             ]
36805         };
36806         
36807         if(this.href.length){
36808             cfg.href = this.href;
36809         }
36810         
36811         var cn = cfg.cn[0].cn;
36812         
36813         if(this.title.length){
36814             cn.push({
36815                 tag: 'h4',
36816                 cls: 'roo-brick-title',
36817                 html: this.title
36818             });
36819         }
36820         
36821         if(this.html.length){
36822             cn.push({
36823                 tag: 'p',
36824                 cls: 'roo-brick-text',
36825                 html: this.html
36826             });
36827         } else {
36828             cn.cls += ' hide';
36829         }
36830         
36831         if(this.bgimage.length){
36832             cfg.cn.push({
36833                 tag: 'img',
36834                 cls: 'roo-brick-image-view',
36835                 src: this.bgimage
36836             });
36837         }
36838         
36839         return cfg;
36840     },
36841     
36842     initEvents: function() 
36843     {
36844         if(this.title.length || this.html.length){
36845             this.el.on('mouseenter'  ,this.enter, this);
36846             this.el.on('mouseleave', this.leave, this);
36847         }
36848         
36849         Roo.EventManager.onWindowResize(this.resize, this); 
36850         
36851         if(this.bgimage.length){
36852             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36853             this.imageEl.on('load', this.onImageLoad, this);
36854             return;
36855         }
36856         
36857         this.resize();
36858     },
36859     
36860     onImageLoad : function()
36861     {
36862         this.resize();
36863     },
36864     
36865     resize : function()
36866     {
36867         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36868         
36869         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36870         
36871         if(this.bgimage.length){
36872             var image = this.el.select('.roo-brick-image-view', true).first();
36873             
36874             image.setWidth(paragraph.getWidth());
36875             
36876             if(this.square){
36877                 image.setHeight(paragraph.getWidth());
36878             }
36879             
36880             this.el.setHeight(image.getHeight());
36881             paragraph.setHeight(image.getHeight());
36882             
36883         }
36884         
36885     },
36886     
36887     enter: function(e, el)
36888     {
36889         e.preventDefault();
36890         
36891         if(this.bgimage.length){
36892             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36893             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36894         }
36895     },
36896     
36897     leave: function(e, el)
36898     {
36899         e.preventDefault();
36900         
36901         if(this.bgimage.length){
36902             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36903             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36904         }
36905     }
36906     
36907 });
36908
36909  
36910
36911  /*
36912  * - LGPL
36913  *
36914  * Number field 
36915  */
36916
36917 /**
36918  * @class Roo.bootstrap.NumberField
36919  * @extends Roo.bootstrap.Input
36920  * Bootstrap NumberField class
36921  * 
36922  * 
36923  * 
36924  * 
36925  * @constructor
36926  * Create a new NumberField
36927  * @param {Object} config The config object
36928  */
36929
36930 Roo.bootstrap.NumberField = function(config){
36931     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36932 };
36933
36934 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36935     
36936     /**
36937      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36938      */
36939     allowDecimals : true,
36940     /**
36941      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36942      */
36943     decimalSeparator : ".",
36944     /**
36945      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36946      */
36947     decimalPrecision : 2,
36948     /**
36949      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36950      */
36951     allowNegative : true,
36952     
36953     /**
36954      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36955      */
36956     allowZero: true,
36957     /**
36958      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36959      */
36960     minValue : Number.NEGATIVE_INFINITY,
36961     /**
36962      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36963      */
36964     maxValue : Number.MAX_VALUE,
36965     /**
36966      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36967      */
36968     minText : "The minimum value for this field is {0}",
36969     /**
36970      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36971      */
36972     maxText : "The maximum value for this field is {0}",
36973     /**
36974      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36975      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36976      */
36977     nanText : "{0} is not a valid number",
36978     /**
36979      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36980      */
36981     thousandsDelimiter : false,
36982     /**
36983      * @cfg {String} valueAlign alignment of value
36984      */
36985     valueAlign : "left",
36986
36987     getAutoCreate : function()
36988     {
36989         var hiddenInput = {
36990             tag: 'input',
36991             type: 'hidden',
36992             id: Roo.id(),
36993             cls: 'hidden-number-input'
36994         };
36995         
36996         if (this.name) {
36997             hiddenInput.name = this.name;
36998         }
36999         
37000         this.name = '';
37001         
37002         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37003         
37004         this.name = hiddenInput.name;
37005         
37006         if(cfg.cn.length > 0) {
37007             cfg.cn.push(hiddenInput);
37008         }
37009         
37010         return cfg;
37011     },
37012
37013     // private
37014     initEvents : function()
37015     {   
37016         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37017         
37018         var allowed = "0123456789";
37019         
37020         if(this.allowDecimals){
37021             allowed += this.decimalSeparator;
37022         }
37023         
37024         if(this.allowNegative){
37025             allowed += "-";
37026         }
37027         
37028         if(this.thousandsDelimiter) {
37029             allowed += ",";
37030         }
37031         
37032         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37033         
37034         var keyPress = function(e){
37035             
37036             var k = e.getKey();
37037             
37038             var c = e.getCharCode();
37039             
37040             if(
37041                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37042                     allowed.indexOf(String.fromCharCode(c)) === -1
37043             ){
37044                 e.stopEvent();
37045                 return;
37046             }
37047             
37048             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37049                 return;
37050             }
37051             
37052             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37053                 e.stopEvent();
37054             }
37055         };
37056         
37057         this.el.on("keypress", keyPress, this);
37058     },
37059     
37060     validateValue : function(value)
37061     {
37062         
37063         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37064             return false;
37065         }
37066         
37067         var num = this.parseValue(value);
37068         
37069         if(isNaN(num)){
37070             this.markInvalid(String.format(this.nanText, value));
37071             return false;
37072         }
37073         
37074         if(num < this.minValue){
37075             this.markInvalid(String.format(this.minText, this.minValue));
37076             return false;
37077         }
37078         
37079         if(num > this.maxValue){
37080             this.markInvalid(String.format(this.maxText, this.maxValue));
37081             return false;
37082         }
37083         
37084         return true;
37085     },
37086
37087     getValue : function()
37088     {
37089         var v = this.hiddenEl().getValue();
37090         
37091         return this.fixPrecision(this.parseValue(v));
37092     },
37093
37094     parseValue : function(value)
37095     {
37096         if(this.thousandsDelimiter) {
37097             value += "";
37098             r = new RegExp(",", "g");
37099             value = value.replace(r, "");
37100         }
37101         
37102         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37103         return isNaN(value) ? '' : value;
37104     },
37105
37106     fixPrecision : function(value)
37107     {
37108         if(this.thousandsDelimiter) {
37109             value += "";
37110             r = new RegExp(",", "g");
37111             value = value.replace(r, "");
37112         }
37113         
37114         var nan = isNaN(value);
37115         
37116         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37117             return nan ? '' : value;
37118         }
37119         return parseFloat(value).toFixed(this.decimalPrecision);
37120     },
37121
37122     setValue : function(v)
37123     {
37124         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37125         
37126         this.value = v;
37127         
37128         if(this.rendered){
37129             
37130             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37131             
37132             this.inputEl().dom.value = (v == '') ? '' :
37133                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37134             
37135             if(!this.allowZero && v === '0') {
37136                 this.hiddenEl().dom.value = '';
37137                 this.inputEl().dom.value = '';
37138             }
37139             
37140             this.validate();
37141         }
37142     },
37143
37144     decimalPrecisionFcn : function(v)
37145     {
37146         return Math.floor(v);
37147     },
37148
37149     beforeBlur : function()
37150     {
37151         var v = this.parseValue(this.getRawValue());
37152         
37153         if(v || v === 0 || v === ''){
37154             this.setValue(v);
37155         }
37156     },
37157     
37158     hiddenEl : function()
37159     {
37160         return this.el.select('input.hidden-number-input',true).first();
37161     }
37162     
37163 });
37164
37165  
37166
37167 /*
37168 * Licence: LGPL
37169 */
37170
37171 /**
37172  * @class Roo.bootstrap.DocumentSlider
37173  * @extends Roo.bootstrap.Component
37174  * Bootstrap DocumentSlider class
37175  * 
37176  * @constructor
37177  * Create a new DocumentViewer
37178  * @param {Object} config The config object
37179  */
37180
37181 Roo.bootstrap.DocumentSlider = function(config){
37182     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37183     
37184     this.files = [];
37185     
37186     this.addEvents({
37187         /**
37188          * @event initial
37189          * Fire after initEvent
37190          * @param {Roo.bootstrap.DocumentSlider} this
37191          */
37192         "initial" : true,
37193         /**
37194          * @event update
37195          * Fire after update
37196          * @param {Roo.bootstrap.DocumentSlider} this
37197          */
37198         "update" : true,
37199         /**
37200          * @event click
37201          * Fire after click
37202          * @param {Roo.bootstrap.DocumentSlider} this
37203          */
37204         "click" : true
37205     });
37206 };
37207
37208 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37209     
37210     files : false,
37211     
37212     indicator : 0,
37213     
37214     getAutoCreate : function()
37215     {
37216         var cfg = {
37217             tag : 'div',
37218             cls : 'roo-document-slider',
37219             cn : [
37220                 {
37221                     tag : 'div',
37222                     cls : 'roo-document-slider-header',
37223                     cn : [
37224                         {
37225                             tag : 'div',
37226                             cls : 'roo-document-slider-header-title'
37227                         }
37228                     ]
37229                 },
37230                 {
37231                     tag : 'div',
37232                     cls : 'roo-document-slider-body',
37233                     cn : [
37234                         {
37235                             tag : 'div',
37236                             cls : 'roo-document-slider-prev',
37237                             cn : [
37238                                 {
37239                                     tag : 'i',
37240                                     cls : 'fa fa-chevron-left'
37241                                 }
37242                             ]
37243                         },
37244                         {
37245                             tag : 'div',
37246                             cls : 'roo-document-slider-thumb',
37247                             cn : [
37248                                 {
37249                                     tag : 'img',
37250                                     cls : 'roo-document-slider-image'
37251                                 }
37252                             ]
37253                         },
37254                         {
37255                             tag : 'div',
37256                             cls : 'roo-document-slider-next',
37257                             cn : [
37258                                 {
37259                                     tag : 'i',
37260                                     cls : 'fa fa-chevron-right'
37261                                 }
37262                             ]
37263                         }
37264                     ]
37265                 }
37266             ]
37267         };
37268         
37269         return cfg;
37270     },
37271     
37272     initEvents : function()
37273     {
37274         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37275         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37276         
37277         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37278         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37279         
37280         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37281         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37282         
37283         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37284         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37285         
37286         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37287         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37288         
37289         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37290         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37291         
37292         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37293         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37294         
37295         this.thumbEl.on('click', this.onClick, this);
37296         
37297         this.prevIndicator.on('click', this.prev, this);
37298         
37299         this.nextIndicator.on('click', this.next, this);
37300         
37301     },
37302     
37303     initial : function()
37304     {
37305         if(this.files.length){
37306             this.indicator = 1;
37307             this.update()
37308         }
37309         
37310         this.fireEvent('initial', this);
37311     },
37312     
37313     update : function()
37314     {
37315         this.imageEl.attr('src', this.files[this.indicator - 1]);
37316         
37317         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37318         
37319         this.prevIndicator.show();
37320         
37321         if(this.indicator == 1){
37322             this.prevIndicator.hide();
37323         }
37324         
37325         this.nextIndicator.show();
37326         
37327         if(this.indicator == this.files.length){
37328             this.nextIndicator.hide();
37329         }
37330         
37331         this.thumbEl.scrollTo('top');
37332         
37333         this.fireEvent('update', this);
37334     },
37335     
37336     onClick : function(e)
37337     {
37338         e.preventDefault();
37339         
37340         this.fireEvent('click', this);
37341     },
37342     
37343     prev : function(e)
37344     {
37345         e.preventDefault();
37346         
37347         this.indicator = Math.max(1, this.indicator - 1);
37348         
37349         this.update();
37350     },
37351     
37352     next : function(e)
37353     {
37354         e.preventDefault();
37355         
37356         this.indicator = Math.min(this.files.length, this.indicator + 1);
37357         
37358         this.update();
37359     }
37360 });
37361 /*
37362  * - LGPL
37363  *
37364  * RadioSet
37365  *
37366  *
37367  */
37368
37369 /**
37370  * @class Roo.bootstrap.RadioSet
37371  * @extends Roo.bootstrap.Input
37372  * Bootstrap RadioSet class
37373  * @cfg {String} indicatorpos (left|right) default left
37374  * @cfg {Boolean} inline (true|false) inline the element (default true)
37375  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37376  * @constructor
37377  * Create a new RadioSet
37378  * @param {Object} config The config object
37379  */
37380
37381 Roo.bootstrap.RadioSet = function(config){
37382     
37383     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37384     
37385     this.radioes = [];
37386     
37387     Roo.bootstrap.RadioSet.register(this);
37388     
37389     this.addEvents({
37390         /**
37391         * @event check
37392         * Fires when the element is checked or unchecked.
37393         * @param {Roo.bootstrap.RadioSet} this This radio
37394         * @param {Roo.bootstrap.Radio} item The checked item
37395         */
37396        check : true,
37397        /**
37398         * @event click
37399         * Fires when the element is click.
37400         * @param {Roo.bootstrap.RadioSet} this This radio set
37401         * @param {Roo.bootstrap.Radio} item The checked item
37402         * @param {Roo.EventObject} e The event object
37403         */
37404        click : true
37405     });
37406     
37407 };
37408
37409 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37410
37411     radioes : false,
37412     
37413     inline : true,
37414     
37415     weight : '',
37416     
37417     indicatorpos : 'left',
37418     
37419     getAutoCreate : function()
37420     {
37421         var label = {
37422             tag : 'label',
37423             cls : 'roo-radio-set-label',
37424             cn : [
37425                 {
37426                     tag : 'span',
37427                     html : this.fieldLabel
37428                 }
37429             ]
37430         };
37431         if (Roo.bootstrap.version == 3) {
37432             
37433             
37434             if(this.indicatorpos == 'left'){
37435                 label.cn.unshift({
37436                     tag : 'i',
37437                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37438                     tooltip : 'This field is required'
37439                 });
37440             } else {
37441                 label.cn.push({
37442                     tag : 'i',
37443                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37444                     tooltip : 'This field is required'
37445                 });
37446             }
37447         }
37448         var items = {
37449             tag : 'div',
37450             cls : 'roo-radio-set-items'
37451         };
37452         
37453         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37454         
37455         if (align === 'left' && this.fieldLabel.length) {
37456             
37457             items = {
37458                 cls : "roo-radio-set-right", 
37459                 cn: [
37460                     items
37461                 ]
37462             };
37463             
37464             if(this.labelWidth > 12){
37465                 label.style = "width: " + this.labelWidth + 'px';
37466             }
37467             
37468             if(this.labelWidth < 13 && this.labelmd == 0){
37469                 this.labelmd = this.labelWidth;
37470             }
37471             
37472             if(this.labellg > 0){
37473                 label.cls += ' col-lg-' + this.labellg;
37474                 items.cls += ' col-lg-' + (12 - this.labellg);
37475             }
37476             
37477             if(this.labelmd > 0){
37478                 label.cls += ' col-md-' + this.labelmd;
37479                 items.cls += ' col-md-' + (12 - this.labelmd);
37480             }
37481             
37482             if(this.labelsm > 0){
37483                 label.cls += ' col-sm-' + this.labelsm;
37484                 items.cls += ' col-sm-' + (12 - this.labelsm);
37485             }
37486             
37487             if(this.labelxs > 0){
37488                 label.cls += ' col-xs-' + this.labelxs;
37489                 items.cls += ' col-xs-' + (12 - this.labelxs);
37490             }
37491         }
37492         
37493         var cfg = {
37494             tag : 'div',
37495             cls : 'roo-radio-set',
37496             cn : [
37497                 {
37498                     tag : 'input',
37499                     cls : 'roo-radio-set-input',
37500                     type : 'hidden',
37501                     name : this.name,
37502                     value : this.value ? this.value :  ''
37503                 },
37504                 label,
37505                 items
37506             ]
37507         };
37508         
37509         if(this.weight.length){
37510             cfg.cls += ' roo-radio-' + this.weight;
37511         }
37512         
37513         if(this.inline) {
37514             cfg.cls += ' roo-radio-set-inline';
37515         }
37516         
37517         var settings=this;
37518         ['xs','sm','md','lg'].map(function(size){
37519             if (settings[size]) {
37520                 cfg.cls += ' col-' + size + '-' + settings[size];
37521             }
37522         });
37523         
37524         return cfg;
37525         
37526     },
37527
37528     initEvents : function()
37529     {
37530         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37531         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37532         
37533         if(!this.fieldLabel.length){
37534             this.labelEl.hide();
37535         }
37536         
37537         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37538         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37539         
37540         this.indicator = this.indicatorEl();
37541         
37542         if(this.indicator){
37543             this.indicator.addClass('invisible');
37544         }
37545         
37546         this.originalValue = this.getValue();
37547         
37548     },
37549     
37550     inputEl: function ()
37551     {
37552         return this.el.select('.roo-radio-set-input', true).first();
37553     },
37554     
37555     getChildContainer : function()
37556     {
37557         return this.itemsEl;
37558     },
37559     
37560     register : function(item)
37561     {
37562         this.radioes.push(item);
37563         
37564     },
37565     
37566     validate : function()
37567     {   
37568         if(this.getVisibilityEl().hasClass('hidden')){
37569             return true;
37570         }
37571         
37572         var valid = false;
37573         
37574         Roo.each(this.radioes, function(i){
37575             if(!i.checked){
37576                 return;
37577             }
37578             
37579             valid = true;
37580             return false;
37581         });
37582         
37583         if(this.allowBlank) {
37584             return true;
37585         }
37586         
37587         if(this.disabled || valid){
37588             this.markValid();
37589             return true;
37590         }
37591         
37592         this.markInvalid();
37593         return false;
37594         
37595     },
37596     
37597     markValid : function()
37598     {
37599         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37600             this.indicatorEl().removeClass('visible');
37601             this.indicatorEl().addClass('invisible');
37602         }
37603         
37604         
37605         if (Roo.bootstrap.version == 3) {
37606             this.el.removeClass([this.invalidClass, this.validClass]);
37607             this.el.addClass(this.validClass);
37608         } else {
37609             this.el.removeClass(['is-invalid','is-valid']);
37610             this.el.addClass(['is-valid']);
37611         }
37612         this.fireEvent('valid', this);
37613     },
37614     
37615     markInvalid : function(msg)
37616     {
37617         if(this.allowBlank || this.disabled){
37618             return;
37619         }
37620         
37621         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37622             this.indicatorEl().removeClass('invisible');
37623             this.indicatorEl().addClass('visible');
37624         }
37625         if (Roo.bootstrap.version == 3) {
37626             this.el.removeClass([this.invalidClass, this.validClass]);
37627             this.el.addClass(this.invalidClass);
37628         } else {
37629             this.el.removeClass(['is-invalid','is-valid']);
37630             this.el.addClass(['is-invalid']);
37631         }
37632         
37633         this.fireEvent('invalid', this, msg);
37634         
37635     },
37636     
37637     setValue : function(v, suppressEvent)
37638     {   
37639         if(this.value === v){
37640             return;
37641         }
37642         
37643         this.value = v;
37644         
37645         if(this.rendered){
37646             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37647         }
37648         
37649         Roo.each(this.radioes, function(i){
37650             i.checked = false;
37651             i.el.removeClass('checked');
37652         });
37653         
37654         Roo.each(this.radioes, function(i){
37655             
37656             if(i.value === v || i.value.toString() === v.toString()){
37657                 i.checked = true;
37658                 i.el.addClass('checked');
37659                 
37660                 if(suppressEvent !== true){
37661                     this.fireEvent('check', this, i);
37662                 }
37663                 
37664                 return false;
37665             }
37666             
37667         }, this);
37668         
37669         this.validate();
37670     },
37671     
37672     clearInvalid : function(){
37673         
37674         if(!this.el || this.preventMark){
37675             return;
37676         }
37677         
37678         this.el.removeClass([this.invalidClass]);
37679         
37680         this.fireEvent('valid', this);
37681     }
37682     
37683 });
37684
37685 Roo.apply(Roo.bootstrap.RadioSet, {
37686     
37687     groups: {},
37688     
37689     register : function(set)
37690     {
37691         this.groups[set.name] = set;
37692     },
37693     
37694     get: function(name) 
37695     {
37696         if (typeof(this.groups[name]) == 'undefined') {
37697             return false;
37698         }
37699         
37700         return this.groups[name] ;
37701     }
37702     
37703 });
37704 /*
37705  * Based on:
37706  * Ext JS Library 1.1.1
37707  * Copyright(c) 2006-2007, Ext JS, LLC.
37708  *
37709  * Originally Released Under LGPL - original licence link has changed is not relivant.
37710  *
37711  * Fork - LGPL
37712  * <script type="text/javascript">
37713  */
37714
37715
37716 /**
37717  * @class Roo.bootstrap.SplitBar
37718  * @extends Roo.util.Observable
37719  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37720  * <br><br>
37721  * Usage:
37722  * <pre><code>
37723 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37724                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37725 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37726 split.minSize = 100;
37727 split.maxSize = 600;
37728 split.animate = true;
37729 split.on('moved', splitterMoved);
37730 </code></pre>
37731  * @constructor
37732  * Create a new SplitBar
37733  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37734  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37735  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37736  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37737                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37738                         position of the SplitBar).
37739  */
37740 Roo.bootstrap.SplitBar = function(cfg){
37741     
37742     /** @private */
37743     
37744     //{
37745     //  dragElement : elm
37746     //  resizingElement: el,
37747         // optional..
37748     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37749     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37750         // existingProxy ???
37751     //}
37752     
37753     this.el = Roo.get(cfg.dragElement, true);
37754     this.el.dom.unselectable = "on";
37755     /** @private */
37756     this.resizingEl = Roo.get(cfg.resizingElement, true);
37757
37758     /**
37759      * @private
37760      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37761      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37762      * @type Number
37763      */
37764     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37765     
37766     /**
37767      * The minimum size of the resizing element. (Defaults to 0)
37768      * @type Number
37769      */
37770     this.minSize = 0;
37771     
37772     /**
37773      * The maximum size of the resizing element. (Defaults to 2000)
37774      * @type Number
37775      */
37776     this.maxSize = 2000;
37777     
37778     /**
37779      * Whether to animate the transition to the new size
37780      * @type Boolean
37781      */
37782     this.animate = false;
37783     
37784     /**
37785      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37786      * @type Boolean
37787      */
37788     this.useShim = false;
37789     
37790     /** @private */
37791     this.shim = null;
37792     
37793     if(!cfg.existingProxy){
37794         /** @private */
37795         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37796     }else{
37797         this.proxy = Roo.get(cfg.existingProxy).dom;
37798     }
37799     /** @private */
37800     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37801     
37802     /** @private */
37803     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37804     
37805     /** @private */
37806     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37807     
37808     /** @private */
37809     this.dragSpecs = {};
37810     
37811     /**
37812      * @private The adapter to use to positon and resize elements
37813      */
37814     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37815     this.adapter.init(this);
37816     
37817     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37818         /** @private */
37819         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37820         this.el.addClass("roo-splitbar-h");
37821     }else{
37822         /** @private */
37823         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37824         this.el.addClass("roo-splitbar-v");
37825     }
37826     
37827     this.addEvents({
37828         /**
37829          * @event resize
37830          * Fires when the splitter is moved (alias for {@link #event-moved})
37831          * @param {Roo.bootstrap.SplitBar} this
37832          * @param {Number} newSize the new width or height
37833          */
37834         "resize" : true,
37835         /**
37836          * @event moved
37837          * Fires when the splitter is moved
37838          * @param {Roo.bootstrap.SplitBar} this
37839          * @param {Number} newSize the new width or height
37840          */
37841         "moved" : true,
37842         /**
37843          * @event beforeresize
37844          * Fires before the splitter is dragged
37845          * @param {Roo.bootstrap.SplitBar} this
37846          */
37847         "beforeresize" : true,
37848
37849         "beforeapply" : true
37850     });
37851
37852     Roo.util.Observable.call(this);
37853 };
37854
37855 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37856     onStartProxyDrag : function(x, y){
37857         this.fireEvent("beforeresize", this);
37858         if(!this.overlay){
37859             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37860             o.unselectable();
37861             o.enableDisplayMode("block");
37862             // all splitbars share the same overlay
37863             Roo.bootstrap.SplitBar.prototype.overlay = o;
37864         }
37865         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37866         this.overlay.show();
37867         Roo.get(this.proxy).setDisplayed("block");
37868         var size = this.adapter.getElementSize(this);
37869         this.activeMinSize = this.getMinimumSize();;
37870         this.activeMaxSize = this.getMaximumSize();;
37871         var c1 = size - this.activeMinSize;
37872         var c2 = Math.max(this.activeMaxSize - size, 0);
37873         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37874             this.dd.resetConstraints();
37875             this.dd.setXConstraint(
37876                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37877                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37878             );
37879             this.dd.setYConstraint(0, 0);
37880         }else{
37881             this.dd.resetConstraints();
37882             this.dd.setXConstraint(0, 0);
37883             this.dd.setYConstraint(
37884                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37885                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37886             );
37887          }
37888         this.dragSpecs.startSize = size;
37889         this.dragSpecs.startPoint = [x, y];
37890         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37891     },
37892     
37893     /** 
37894      * @private Called after the drag operation by the DDProxy
37895      */
37896     onEndProxyDrag : function(e){
37897         Roo.get(this.proxy).setDisplayed(false);
37898         var endPoint = Roo.lib.Event.getXY(e);
37899         if(this.overlay){
37900             this.overlay.hide();
37901         }
37902         var newSize;
37903         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37904             newSize = this.dragSpecs.startSize + 
37905                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37906                     endPoint[0] - this.dragSpecs.startPoint[0] :
37907                     this.dragSpecs.startPoint[0] - endPoint[0]
37908                 );
37909         }else{
37910             newSize = this.dragSpecs.startSize + 
37911                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37912                     endPoint[1] - this.dragSpecs.startPoint[1] :
37913                     this.dragSpecs.startPoint[1] - endPoint[1]
37914                 );
37915         }
37916         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37917         if(newSize != this.dragSpecs.startSize){
37918             if(this.fireEvent('beforeapply', this, newSize) !== false){
37919                 this.adapter.setElementSize(this, newSize);
37920                 this.fireEvent("moved", this, newSize);
37921                 this.fireEvent("resize", this, newSize);
37922             }
37923         }
37924     },
37925     
37926     /**
37927      * Get the adapter this SplitBar uses
37928      * @return The adapter object
37929      */
37930     getAdapter : function(){
37931         return this.adapter;
37932     },
37933     
37934     /**
37935      * Set the adapter this SplitBar uses
37936      * @param {Object} adapter A SplitBar adapter object
37937      */
37938     setAdapter : function(adapter){
37939         this.adapter = adapter;
37940         this.adapter.init(this);
37941     },
37942     
37943     /**
37944      * Gets the minimum size for the resizing element
37945      * @return {Number} The minimum size
37946      */
37947     getMinimumSize : function(){
37948         return this.minSize;
37949     },
37950     
37951     /**
37952      * Sets the minimum size for the resizing element
37953      * @param {Number} minSize The minimum size
37954      */
37955     setMinimumSize : function(minSize){
37956         this.minSize = minSize;
37957     },
37958     
37959     /**
37960      * Gets the maximum size for the resizing element
37961      * @return {Number} The maximum size
37962      */
37963     getMaximumSize : function(){
37964         return this.maxSize;
37965     },
37966     
37967     /**
37968      * Sets the maximum size for the resizing element
37969      * @param {Number} maxSize The maximum size
37970      */
37971     setMaximumSize : function(maxSize){
37972         this.maxSize = maxSize;
37973     },
37974     
37975     /**
37976      * Sets the initialize size for the resizing element
37977      * @param {Number} size The initial size
37978      */
37979     setCurrentSize : function(size){
37980         var oldAnimate = this.animate;
37981         this.animate = false;
37982         this.adapter.setElementSize(this, size);
37983         this.animate = oldAnimate;
37984     },
37985     
37986     /**
37987      * Destroy this splitbar. 
37988      * @param {Boolean} removeEl True to remove the element
37989      */
37990     destroy : function(removeEl){
37991         if(this.shim){
37992             this.shim.remove();
37993         }
37994         this.dd.unreg();
37995         this.proxy.parentNode.removeChild(this.proxy);
37996         if(removeEl){
37997             this.el.remove();
37998         }
37999     }
38000 });
38001
38002 /**
38003  * @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.
38004  */
38005 Roo.bootstrap.SplitBar.createProxy = function(dir){
38006     var proxy = new Roo.Element(document.createElement("div"));
38007     proxy.unselectable();
38008     var cls = 'roo-splitbar-proxy';
38009     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38010     document.body.appendChild(proxy.dom);
38011     return proxy.dom;
38012 };
38013
38014 /** 
38015  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38016  * Default Adapter. It assumes the splitter and resizing element are not positioned
38017  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38018  */
38019 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38020 };
38021
38022 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38023     // do nothing for now
38024     init : function(s){
38025     
38026     },
38027     /**
38028      * Called before drag operations to get the current size of the resizing element. 
38029      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38030      */
38031      getElementSize : function(s){
38032         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38033             return s.resizingEl.getWidth();
38034         }else{
38035             return s.resizingEl.getHeight();
38036         }
38037     },
38038     
38039     /**
38040      * Called after drag operations to set the size of the resizing element.
38041      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38042      * @param {Number} newSize The new size to set
38043      * @param {Function} onComplete A function to be invoked when resizing is complete
38044      */
38045     setElementSize : function(s, newSize, onComplete){
38046         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38047             if(!s.animate){
38048                 s.resizingEl.setWidth(newSize);
38049                 if(onComplete){
38050                     onComplete(s, newSize);
38051                 }
38052             }else{
38053                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38054             }
38055         }else{
38056             
38057             if(!s.animate){
38058                 s.resizingEl.setHeight(newSize);
38059                 if(onComplete){
38060                     onComplete(s, newSize);
38061                 }
38062             }else{
38063                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38064             }
38065         }
38066     }
38067 };
38068
38069 /** 
38070  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38071  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38072  * Adapter that  moves the splitter element to align with the resized sizing element. 
38073  * Used with an absolute positioned SplitBar.
38074  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38075  * document.body, make sure you assign an id to the body element.
38076  */
38077 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38078     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38079     this.container = Roo.get(container);
38080 };
38081
38082 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38083     init : function(s){
38084         this.basic.init(s);
38085     },
38086     
38087     getElementSize : function(s){
38088         return this.basic.getElementSize(s);
38089     },
38090     
38091     setElementSize : function(s, newSize, onComplete){
38092         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38093     },
38094     
38095     moveSplitter : function(s){
38096         var yes = Roo.bootstrap.SplitBar;
38097         switch(s.placement){
38098             case yes.LEFT:
38099                 s.el.setX(s.resizingEl.getRight());
38100                 break;
38101             case yes.RIGHT:
38102                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38103                 break;
38104             case yes.TOP:
38105                 s.el.setY(s.resizingEl.getBottom());
38106                 break;
38107             case yes.BOTTOM:
38108                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38109                 break;
38110         }
38111     }
38112 };
38113
38114 /**
38115  * Orientation constant - Create a vertical SplitBar
38116  * @static
38117  * @type Number
38118  */
38119 Roo.bootstrap.SplitBar.VERTICAL = 1;
38120
38121 /**
38122  * Orientation constant - Create a horizontal SplitBar
38123  * @static
38124  * @type Number
38125  */
38126 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38127
38128 /**
38129  * Placement constant - The resizing element is to the left of the splitter element
38130  * @static
38131  * @type Number
38132  */
38133 Roo.bootstrap.SplitBar.LEFT = 1;
38134
38135 /**
38136  * Placement constant - The resizing element is to the right of the splitter element
38137  * @static
38138  * @type Number
38139  */
38140 Roo.bootstrap.SplitBar.RIGHT = 2;
38141
38142 /**
38143  * Placement constant - The resizing element is positioned above the splitter element
38144  * @static
38145  * @type Number
38146  */
38147 Roo.bootstrap.SplitBar.TOP = 3;
38148
38149 /**
38150  * Placement constant - The resizing element is positioned under splitter element
38151  * @static
38152  * @type Number
38153  */
38154 Roo.bootstrap.SplitBar.BOTTOM = 4;
38155 Roo.namespace("Roo.bootstrap.layout");/*
38156  * Based on:
38157  * Ext JS Library 1.1.1
38158  * Copyright(c) 2006-2007, Ext JS, LLC.
38159  *
38160  * Originally Released Under LGPL - original licence link has changed is not relivant.
38161  *
38162  * Fork - LGPL
38163  * <script type="text/javascript">
38164  */
38165
38166 /**
38167  * @class Roo.bootstrap.layout.Manager
38168  * @extends Roo.bootstrap.Component
38169  * Base class for layout managers.
38170  */
38171 Roo.bootstrap.layout.Manager = function(config)
38172 {
38173     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38174
38175
38176
38177
38178
38179     /** false to disable window resize monitoring @type Boolean */
38180     this.monitorWindowResize = true;
38181     this.regions = {};
38182     this.addEvents({
38183         /**
38184          * @event layout
38185          * Fires when a layout is performed.
38186          * @param {Roo.LayoutManager} this
38187          */
38188         "layout" : true,
38189         /**
38190          * @event regionresized
38191          * Fires when the user resizes a region.
38192          * @param {Roo.LayoutRegion} region The resized region
38193          * @param {Number} newSize The new size (width for east/west, height for north/south)
38194          */
38195         "regionresized" : true,
38196         /**
38197          * @event regioncollapsed
38198          * Fires when a region is collapsed.
38199          * @param {Roo.LayoutRegion} region The collapsed region
38200          */
38201         "regioncollapsed" : true,
38202         /**
38203          * @event regionexpanded
38204          * Fires when a region is expanded.
38205          * @param {Roo.LayoutRegion} region The expanded region
38206          */
38207         "regionexpanded" : true
38208     });
38209     this.updating = false;
38210
38211     if (config.el) {
38212         this.el = Roo.get(config.el);
38213         this.initEvents();
38214     }
38215
38216 };
38217
38218 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38219
38220
38221     regions : null,
38222
38223     monitorWindowResize : true,
38224
38225
38226     updating : false,
38227
38228
38229     onRender : function(ct, position)
38230     {
38231         if(!this.el){
38232             this.el = Roo.get(ct);
38233             this.initEvents();
38234         }
38235         //this.fireEvent('render',this);
38236     },
38237
38238
38239     initEvents: function()
38240     {
38241
38242
38243         // ie scrollbar fix
38244         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38245             document.body.scroll = "no";
38246         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38247             this.el.position('relative');
38248         }
38249         this.id = this.el.id;
38250         this.el.addClass("roo-layout-container");
38251         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38252         if(this.el.dom != document.body ) {
38253             this.el.on('resize', this.layout,this);
38254             this.el.on('show', this.layout,this);
38255         }
38256
38257     },
38258
38259     /**
38260      * Returns true if this layout is currently being updated
38261      * @return {Boolean}
38262      */
38263     isUpdating : function(){
38264         return this.updating;
38265     },
38266
38267     /**
38268      * Suspend the LayoutManager from doing auto-layouts while
38269      * making multiple add or remove calls
38270      */
38271     beginUpdate : function(){
38272         this.updating = true;
38273     },
38274
38275     /**
38276      * Restore auto-layouts and optionally disable the manager from performing a layout
38277      * @param {Boolean} noLayout true to disable a layout update
38278      */
38279     endUpdate : function(noLayout){
38280         this.updating = false;
38281         if(!noLayout){
38282             this.layout();
38283         }
38284     },
38285
38286     layout: function(){
38287         // abstract...
38288     },
38289
38290     onRegionResized : function(region, newSize){
38291         this.fireEvent("regionresized", region, newSize);
38292         this.layout();
38293     },
38294
38295     onRegionCollapsed : function(region){
38296         this.fireEvent("regioncollapsed", region);
38297     },
38298
38299     onRegionExpanded : function(region){
38300         this.fireEvent("regionexpanded", region);
38301     },
38302
38303     /**
38304      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38305      * performs box-model adjustments.
38306      * @return {Object} The size as an object {width: (the width), height: (the height)}
38307      */
38308     getViewSize : function()
38309     {
38310         var size;
38311         if(this.el.dom != document.body){
38312             size = this.el.getSize();
38313         }else{
38314             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38315         }
38316         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38317         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38318         return size;
38319     },
38320
38321     /**
38322      * Returns the Element this layout is bound to.
38323      * @return {Roo.Element}
38324      */
38325     getEl : function(){
38326         return this.el;
38327     },
38328
38329     /**
38330      * Returns the specified region.
38331      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38332      * @return {Roo.LayoutRegion}
38333      */
38334     getRegion : function(target){
38335         return this.regions[target.toLowerCase()];
38336     },
38337
38338     onWindowResize : function(){
38339         if(this.monitorWindowResize){
38340             this.layout();
38341         }
38342     }
38343 });
38344 /*
38345  * Based on:
38346  * Ext JS Library 1.1.1
38347  * Copyright(c) 2006-2007, Ext JS, LLC.
38348  *
38349  * Originally Released Under LGPL - original licence link has changed is not relivant.
38350  *
38351  * Fork - LGPL
38352  * <script type="text/javascript">
38353  */
38354 /**
38355  * @class Roo.bootstrap.layout.Border
38356  * @extends Roo.bootstrap.layout.Manager
38357  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38358  * please see: examples/bootstrap/nested.html<br><br>
38359  
38360 <b>The container the layout is rendered into can be either the body element or any other element.
38361 If it is not the body element, the container needs to either be an absolute positioned element,
38362 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38363 the container size if it is not the body element.</b>
38364
38365 * @constructor
38366 * Create a new Border
38367 * @param {Object} config Configuration options
38368  */
38369 Roo.bootstrap.layout.Border = function(config){
38370     config = config || {};
38371     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38372     
38373     
38374     
38375     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38376         if(config[region]){
38377             config[region].region = region;
38378             this.addRegion(config[region]);
38379         }
38380     },this);
38381     
38382 };
38383
38384 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38385
38386 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38387     
38388     parent : false, // this might point to a 'nest' or a ???
38389     
38390     /**
38391      * Creates and adds a new region if it doesn't already exist.
38392      * @param {String} target The target region key (north, south, east, west or center).
38393      * @param {Object} config The regions config object
38394      * @return {BorderLayoutRegion} The new region
38395      */
38396     addRegion : function(config)
38397     {
38398         if(!this.regions[config.region]){
38399             var r = this.factory(config);
38400             this.bindRegion(r);
38401         }
38402         return this.regions[config.region];
38403     },
38404
38405     // private (kinda)
38406     bindRegion : function(r){
38407         this.regions[r.config.region] = r;
38408         
38409         r.on("visibilitychange",    this.layout, this);
38410         r.on("paneladded",          this.layout, this);
38411         r.on("panelremoved",        this.layout, this);
38412         r.on("invalidated",         this.layout, this);
38413         r.on("resized",             this.onRegionResized, this);
38414         r.on("collapsed",           this.onRegionCollapsed, this);
38415         r.on("expanded",            this.onRegionExpanded, this);
38416     },
38417
38418     /**
38419      * Performs a layout update.
38420      */
38421     layout : function()
38422     {
38423         if(this.updating) {
38424             return;
38425         }
38426         
38427         // render all the rebions if they have not been done alreayd?
38428         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38429             if(this.regions[region] && !this.regions[region].bodyEl){
38430                 this.regions[region].onRender(this.el)
38431             }
38432         },this);
38433         
38434         var size = this.getViewSize();
38435         var w = size.width;
38436         var h = size.height;
38437         var centerW = w;
38438         var centerH = h;
38439         var centerY = 0;
38440         var centerX = 0;
38441         //var x = 0, y = 0;
38442
38443         var rs = this.regions;
38444         var north = rs["north"];
38445         var south = rs["south"]; 
38446         var west = rs["west"];
38447         var east = rs["east"];
38448         var center = rs["center"];
38449         //if(this.hideOnLayout){ // not supported anymore
38450             //c.el.setStyle("display", "none");
38451         //}
38452         if(north && north.isVisible()){
38453             var b = north.getBox();
38454             var m = north.getMargins();
38455             b.width = w - (m.left+m.right);
38456             b.x = m.left;
38457             b.y = m.top;
38458             centerY = b.height + b.y + m.bottom;
38459             centerH -= centerY;
38460             north.updateBox(this.safeBox(b));
38461         }
38462         if(south && south.isVisible()){
38463             var b = south.getBox();
38464             var m = south.getMargins();
38465             b.width = w - (m.left+m.right);
38466             b.x = m.left;
38467             var totalHeight = (b.height + m.top + m.bottom);
38468             b.y = h - totalHeight + m.top;
38469             centerH -= totalHeight;
38470             south.updateBox(this.safeBox(b));
38471         }
38472         if(west && west.isVisible()){
38473             var b = west.getBox();
38474             var m = west.getMargins();
38475             b.height = centerH - (m.top+m.bottom);
38476             b.x = m.left;
38477             b.y = centerY + m.top;
38478             var totalWidth = (b.width + m.left + m.right);
38479             centerX += totalWidth;
38480             centerW -= totalWidth;
38481             west.updateBox(this.safeBox(b));
38482         }
38483         if(east && east.isVisible()){
38484             var b = east.getBox();
38485             var m = east.getMargins();
38486             b.height = centerH - (m.top+m.bottom);
38487             var totalWidth = (b.width + m.left + m.right);
38488             b.x = w - totalWidth + m.left;
38489             b.y = centerY + m.top;
38490             centerW -= totalWidth;
38491             east.updateBox(this.safeBox(b));
38492         }
38493         if(center){
38494             var m = center.getMargins();
38495             var centerBox = {
38496                 x: centerX + m.left,
38497                 y: centerY + m.top,
38498                 width: centerW - (m.left+m.right),
38499                 height: centerH - (m.top+m.bottom)
38500             };
38501             //if(this.hideOnLayout){
38502                 //center.el.setStyle("display", "block");
38503             //}
38504             center.updateBox(this.safeBox(centerBox));
38505         }
38506         this.el.repaint();
38507         this.fireEvent("layout", this);
38508     },
38509
38510     // private
38511     safeBox : function(box){
38512         box.width = Math.max(0, box.width);
38513         box.height = Math.max(0, box.height);
38514         return box;
38515     },
38516
38517     /**
38518      * Adds a ContentPanel (or subclass) to this layout.
38519      * @param {String} target The target region key (north, south, east, west or center).
38520      * @param {Roo.ContentPanel} panel The panel to add
38521      * @return {Roo.ContentPanel} The added panel
38522      */
38523     add : function(target, panel){
38524          
38525         target = target.toLowerCase();
38526         return this.regions[target].add(panel);
38527     },
38528
38529     /**
38530      * Remove a ContentPanel (or subclass) to this layout.
38531      * @param {String} target The target region key (north, south, east, west or center).
38532      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38533      * @return {Roo.ContentPanel} The removed panel
38534      */
38535     remove : function(target, panel){
38536         target = target.toLowerCase();
38537         return this.regions[target].remove(panel);
38538     },
38539
38540     /**
38541      * Searches all regions for a panel with the specified id
38542      * @param {String} panelId
38543      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38544      */
38545     findPanel : function(panelId){
38546         var rs = this.regions;
38547         for(var target in rs){
38548             if(typeof rs[target] != "function"){
38549                 var p = rs[target].getPanel(panelId);
38550                 if(p){
38551                     return p;
38552                 }
38553             }
38554         }
38555         return null;
38556     },
38557
38558     /**
38559      * Searches all regions for a panel with the specified id and activates (shows) it.
38560      * @param {String/ContentPanel} panelId The panels id or the panel itself
38561      * @return {Roo.ContentPanel} The shown panel or null
38562      */
38563     showPanel : function(panelId) {
38564       var rs = this.regions;
38565       for(var target in rs){
38566          var r = rs[target];
38567          if(typeof r != "function"){
38568             if(r.hasPanel(panelId)){
38569                return r.showPanel(panelId);
38570             }
38571          }
38572       }
38573       return null;
38574    },
38575
38576    /**
38577      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38578      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38579      */
38580    /*
38581     restoreState : function(provider){
38582         if(!provider){
38583             provider = Roo.state.Manager;
38584         }
38585         var sm = new Roo.LayoutStateManager();
38586         sm.init(this, provider);
38587     },
38588 */
38589  
38590  
38591     /**
38592      * Adds a xtype elements to the layout.
38593      * <pre><code>
38594
38595 layout.addxtype({
38596        xtype : 'ContentPanel',
38597        region: 'west',
38598        items: [ .... ]
38599    }
38600 );
38601
38602 layout.addxtype({
38603         xtype : 'NestedLayoutPanel',
38604         region: 'west',
38605         layout: {
38606            center: { },
38607            west: { }   
38608         },
38609         items : [ ... list of content panels or nested layout panels.. ]
38610    }
38611 );
38612 </code></pre>
38613      * @param {Object} cfg Xtype definition of item to add.
38614      */
38615     addxtype : function(cfg)
38616     {
38617         // basically accepts a pannel...
38618         // can accept a layout region..!?!?
38619         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38620         
38621         
38622         // theory?  children can only be panels??
38623         
38624         //if (!cfg.xtype.match(/Panel$/)) {
38625         //    return false;
38626         //}
38627         var ret = false;
38628         
38629         if (typeof(cfg.region) == 'undefined') {
38630             Roo.log("Failed to add Panel, region was not set");
38631             Roo.log(cfg);
38632             return false;
38633         }
38634         var region = cfg.region;
38635         delete cfg.region;
38636         
38637           
38638         var xitems = [];
38639         if (cfg.items) {
38640             xitems = cfg.items;
38641             delete cfg.items;
38642         }
38643         var nb = false;
38644         
38645         if ( region == 'center') {
38646             Roo.log("Center: " + cfg.title);
38647         }
38648         
38649         
38650         switch(cfg.xtype) 
38651         {
38652             case 'Content':  // ContentPanel (el, cfg)
38653             case 'Scroll':  // ContentPanel (el, cfg)
38654             case 'View': 
38655                 cfg.autoCreate = cfg.autoCreate || true;
38656                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38657                 //} else {
38658                 //    var el = this.el.createChild();
38659                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38660                 //}
38661                 
38662                 this.add(region, ret);
38663                 break;
38664             
38665             /*
38666             case 'TreePanel': // our new panel!
38667                 cfg.el = this.el.createChild();
38668                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38669                 this.add(region, ret);
38670                 break;
38671             */
38672             
38673             case 'Nest': 
38674                 // create a new Layout (which is  a Border Layout...
38675                 
38676                 var clayout = cfg.layout;
38677                 clayout.el  = this.el.createChild();
38678                 clayout.items   = clayout.items  || [];
38679                 
38680                 delete cfg.layout;
38681                 
38682                 // replace this exitems with the clayout ones..
38683                 xitems = clayout.items;
38684                  
38685                 // force background off if it's in center...
38686                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38687                     cfg.background = false;
38688                 }
38689                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38690                 
38691                 
38692                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38693                 //console.log('adding nested layout panel '  + cfg.toSource());
38694                 this.add(region, ret);
38695                 nb = {}; /// find first...
38696                 break;
38697             
38698             case 'Grid':
38699                 
38700                 // needs grid and region
38701                 
38702                 //var el = this.getRegion(region).el.createChild();
38703                 /*
38704                  *var el = this.el.createChild();
38705                 // create the grid first...
38706                 cfg.grid.container = el;
38707                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38708                 */
38709                 
38710                 if (region == 'center' && this.active ) {
38711                     cfg.background = false;
38712                 }
38713                 
38714                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38715                 
38716                 this.add(region, ret);
38717                 /*
38718                 if (cfg.background) {
38719                     // render grid on panel activation (if panel background)
38720                     ret.on('activate', function(gp) {
38721                         if (!gp.grid.rendered) {
38722                     //        gp.grid.render(el);
38723                         }
38724                     });
38725                 } else {
38726                   //  cfg.grid.render(el);
38727                 }
38728                 */
38729                 break;
38730            
38731            
38732             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38733                 // it was the old xcomponent building that caused this before.
38734                 // espeically if border is the top element in the tree.
38735                 ret = this;
38736                 break; 
38737                 
38738                     
38739                 
38740                 
38741                 
38742             default:
38743                 /*
38744                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38745                     
38746                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38747                     this.add(region, ret);
38748                 } else {
38749                 */
38750                     Roo.log(cfg);
38751                     throw "Can not add '" + cfg.xtype + "' to Border";
38752                     return null;
38753              
38754                                 
38755              
38756         }
38757         this.beginUpdate();
38758         // add children..
38759         var region = '';
38760         var abn = {};
38761         Roo.each(xitems, function(i)  {
38762             region = nb && i.region ? i.region : false;
38763             
38764             var add = ret.addxtype(i);
38765            
38766             if (region) {
38767                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38768                 if (!i.background) {
38769                     abn[region] = nb[region] ;
38770                 }
38771             }
38772             
38773         });
38774         this.endUpdate();
38775
38776         // make the last non-background panel active..
38777         //if (nb) { Roo.log(abn); }
38778         if (nb) {
38779             
38780             for(var r in abn) {
38781                 region = this.getRegion(r);
38782                 if (region) {
38783                     // tried using nb[r], but it does not work..
38784                      
38785                     region.showPanel(abn[r]);
38786                    
38787                 }
38788             }
38789         }
38790         return ret;
38791         
38792     },
38793     
38794     
38795 // private
38796     factory : function(cfg)
38797     {
38798         
38799         var validRegions = Roo.bootstrap.layout.Border.regions;
38800
38801         var target = cfg.region;
38802         cfg.mgr = this;
38803         
38804         var r = Roo.bootstrap.layout;
38805         Roo.log(target);
38806         switch(target){
38807             case "north":
38808                 return new r.North(cfg);
38809             case "south":
38810                 return new r.South(cfg);
38811             case "east":
38812                 return new r.East(cfg);
38813             case "west":
38814                 return new r.West(cfg);
38815             case "center":
38816                 return new r.Center(cfg);
38817         }
38818         throw 'Layout region "'+target+'" not supported.';
38819     }
38820     
38821     
38822 });
38823  /*
38824  * Based on:
38825  * Ext JS Library 1.1.1
38826  * Copyright(c) 2006-2007, Ext JS, LLC.
38827  *
38828  * Originally Released Under LGPL - original licence link has changed is not relivant.
38829  *
38830  * Fork - LGPL
38831  * <script type="text/javascript">
38832  */
38833  
38834 /**
38835  * @class Roo.bootstrap.layout.Basic
38836  * @extends Roo.util.Observable
38837  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38838  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38839  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38840  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38841  * @cfg {string}   region  the region that it inhabits..
38842  * @cfg {bool}   skipConfig skip config?
38843  * 
38844
38845  */
38846 Roo.bootstrap.layout.Basic = function(config){
38847     
38848     this.mgr = config.mgr;
38849     
38850     this.position = config.region;
38851     
38852     var skipConfig = config.skipConfig;
38853     
38854     this.events = {
38855         /**
38856          * @scope Roo.BasicLayoutRegion
38857          */
38858         
38859         /**
38860          * @event beforeremove
38861          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38862          * @param {Roo.LayoutRegion} this
38863          * @param {Roo.ContentPanel} panel The panel
38864          * @param {Object} e The cancel event object
38865          */
38866         "beforeremove" : true,
38867         /**
38868          * @event invalidated
38869          * Fires when the layout for this region is changed.
38870          * @param {Roo.LayoutRegion} this
38871          */
38872         "invalidated" : true,
38873         /**
38874          * @event visibilitychange
38875          * Fires when this region is shown or hidden 
38876          * @param {Roo.LayoutRegion} this
38877          * @param {Boolean} visibility true or false
38878          */
38879         "visibilitychange" : true,
38880         /**
38881          * @event paneladded
38882          * Fires when a panel is added. 
38883          * @param {Roo.LayoutRegion} this
38884          * @param {Roo.ContentPanel} panel The panel
38885          */
38886         "paneladded" : true,
38887         /**
38888          * @event panelremoved
38889          * Fires when a panel is removed. 
38890          * @param {Roo.LayoutRegion} this
38891          * @param {Roo.ContentPanel} panel The panel
38892          */
38893         "panelremoved" : true,
38894         /**
38895          * @event beforecollapse
38896          * Fires when this region before collapse.
38897          * @param {Roo.LayoutRegion} this
38898          */
38899         "beforecollapse" : true,
38900         /**
38901          * @event collapsed
38902          * Fires when this region is collapsed.
38903          * @param {Roo.LayoutRegion} this
38904          */
38905         "collapsed" : true,
38906         /**
38907          * @event expanded
38908          * Fires when this region is expanded.
38909          * @param {Roo.LayoutRegion} this
38910          */
38911         "expanded" : true,
38912         /**
38913          * @event slideshow
38914          * Fires when this region is slid into view.
38915          * @param {Roo.LayoutRegion} this
38916          */
38917         "slideshow" : true,
38918         /**
38919          * @event slidehide
38920          * Fires when this region slides out of view. 
38921          * @param {Roo.LayoutRegion} this
38922          */
38923         "slidehide" : true,
38924         /**
38925          * @event panelactivated
38926          * Fires when a panel is activated. 
38927          * @param {Roo.LayoutRegion} this
38928          * @param {Roo.ContentPanel} panel The activated panel
38929          */
38930         "panelactivated" : true,
38931         /**
38932          * @event resized
38933          * Fires when the user resizes this region. 
38934          * @param {Roo.LayoutRegion} this
38935          * @param {Number} newSize The new size (width for east/west, height for north/south)
38936          */
38937         "resized" : true
38938     };
38939     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38940     this.panels = new Roo.util.MixedCollection();
38941     this.panels.getKey = this.getPanelId.createDelegate(this);
38942     this.box = null;
38943     this.activePanel = null;
38944     // ensure listeners are added...
38945     
38946     if (config.listeners || config.events) {
38947         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38948             listeners : config.listeners || {},
38949             events : config.events || {}
38950         });
38951     }
38952     
38953     if(skipConfig !== true){
38954         this.applyConfig(config);
38955     }
38956 };
38957
38958 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38959 {
38960     getPanelId : function(p){
38961         return p.getId();
38962     },
38963     
38964     applyConfig : function(config){
38965         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38966         this.config = config;
38967         
38968     },
38969     
38970     /**
38971      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38972      * the width, for horizontal (north, south) the height.
38973      * @param {Number} newSize The new width or height
38974      */
38975     resizeTo : function(newSize){
38976         var el = this.el ? this.el :
38977                  (this.activePanel ? this.activePanel.getEl() : null);
38978         if(el){
38979             switch(this.position){
38980                 case "east":
38981                 case "west":
38982                     el.setWidth(newSize);
38983                     this.fireEvent("resized", this, newSize);
38984                 break;
38985                 case "north":
38986                 case "south":
38987                     el.setHeight(newSize);
38988                     this.fireEvent("resized", this, newSize);
38989                 break;                
38990             }
38991         }
38992     },
38993     
38994     getBox : function(){
38995         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38996     },
38997     
38998     getMargins : function(){
38999         return this.margins;
39000     },
39001     
39002     updateBox : function(box){
39003         this.box = box;
39004         var el = this.activePanel.getEl();
39005         el.dom.style.left = box.x + "px";
39006         el.dom.style.top = box.y + "px";
39007         this.activePanel.setSize(box.width, box.height);
39008     },
39009     
39010     /**
39011      * Returns the container element for this region.
39012      * @return {Roo.Element}
39013      */
39014     getEl : function(){
39015         return this.activePanel;
39016     },
39017     
39018     /**
39019      * Returns true if this region is currently visible.
39020      * @return {Boolean}
39021      */
39022     isVisible : function(){
39023         return this.activePanel ? true : false;
39024     },
39025     
39026     setActivePanel : function(panel){
39027         panel = this.getPanel(panel);
39028         if(this.activePanel && this.activePanel != panel){
39029             this.activePanel.setActiveState(false);
39030             this.activePanel.getEl().setLeftTop(-10000,-10000);
39031         }
39032         this.activePanel = panel;
39033         panel.setActiveState(true);
39034         if(this.box){
39035             panel.setSize(this.box.width, this.box.height);
39036         }
39037         this.fireEvent("panelactivated", this, panel);
39038         this.fireEvent("invalidated");
39039     },
39040     
39041     /**
39042      * Show the specified panel.
39043      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39044      * @return {Roo.ContentPanel} The shown panel or null
39045      */
39046     showPanel : function(panel){
39047         panel = this.getPanel(panel);
39048         if(panel){
39049             this.setActivePanel(panel);
39050         }
39051         return panel;
39052     },
39053     
39054     /**
39055      * Get the active panel for this region.
39056      * @return {Roo.ContentPanel} The active panel or null
39057      */
39058     getActivePanel : function(){
39059         return this.activePanel;
39060     },
39061     
39062     /**
39063      * Add the passed ContentPanel(s)
39064      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39065      * @return {Roo.ContentPanel} The panel added (if only one was added)
39066      */
39067     add : function(panel){
39068         if(arguments.length > 1){
39069             for(var i = 0, len = arguments.length; i < len; i++) {
39070                 this.add(arguments[i]);
39071             }
39072             return null;
39073         }
39074         if(this.hasPanel(panel)){
39075             this.showPanel(panel);
39076             return panel;
39077         }
39078         var el = panel.getEl();
39079         if(el.dom.parentNode != this.mgr.el.dom){
39080             this.mgr.el.dom.appendChild(el.dom);
39081         }
39082         if(panel.setRegion){
39083             panel.setRegion(this);
39084         }
39085         this.panels.add(panel);
39086         el.setStyle("position", "absolute");
39087         if(!panel.background){
39088             this.setActivePanel(panel);
39089             if(this.config.initialSize && this.panels.getCount()==1){
39090                 this.resizeTo(this.config.initialSize);
39091             }
39092         }
39093         this.fireEvent("paneladded", this, panel);
39094         return panel;
39095     },
39096     
39097     /**
39098      * Returns true if the panel is in this region.
39099      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39100      * @return {Boolean}
39101      */
39102     hasPanel : function(panel){
39103         if(typeof panel == "object"){ // must be panel obj
39104             panel = panel.getId();
39105         }
39106         return this.getPanel(panel) ? true : false;
39107     },
39108     
39109     /**
39110      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39111      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39112      * @param {Boolean} preservePanel Overrides the config preservePanel option
39113      * @return {Roo.ContentPanel} The panel that was removed
39114      */
39115     remove : function(panel, preservePanel){
39116         panel = this.getPanel(panel);
39117         if(!panel){
39118             return null;
39119         }
39120         var e = {};
39121         this.fireEvent("beforeremove", this, panel, e);
39122         if(e.cancel === true){
39123             return null;
39124         }
39125         var panelId = panel.getId();
39126         this.panels.removeKey(panelId);
39127         return panel;
39128     },
39129     
39130     /**
39131      * Returns the panel specified or null if it's not in this region.
39132      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39133      * @return {Roo.ContentPanel}
39134      */
39135     getPanel : function(id){
39136         if(typeof id == "object"){ // must be panel obj
39137             return id;
39138         }
39139         return this.panels.get(id);
39140     },
39141     
39142     /**
39143      * Returns this regions position (north/south/east/west/center).
39144      * @return {String} 
39145      */
39146     getPosition: function(){
39147         return this.position;    
39148     }
39149 });/*
39150  * Based on:
39151  * Ext JS Library 1.1.1
39152  * Copyright(c) 2006-2007, Ext JS, LLC.
39153  *
39154  * Originally Released Under LGPL - original licence link has changed is not relivant.
39155  *
39156  * Fork - LGPL
39157  * <script type="text/javascript">
39158  */
39159  
39160 /**
39161  * @class Roo.bootstrap.layout.Region
39162  * @extends Roo.bootstrap.layout.Basic
39163  * This class represents a region in a layout manager.
39164  
39165  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39166  * @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})
39167  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39168  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39169  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39170  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39171  * @cfg {String}    title           The title for the region (overrides panel titles)
39172  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39173  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39174  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39175  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39176  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39177  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39178  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39179  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39180  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39181  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39182
39183  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39184  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39185  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39186  * @cfg {Number}    width           For East/West panels
39187  * @cfg {Number}    height          For North/South panels
39188  * @cfg {Boolean}   split           To show the splitter
39189  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39190  * 
39191  * @cfg {string}   cls             Extra CSS classes to add to region
39192  * 
39193  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39194  * @cfg {string}   region  the region that it inhabits..
39195  *
39196
39197  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39198  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39199
39200  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39201  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39202  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39203  */
39204 Roo.bootstrap.layout.Region = function(config)
39205 {
39206     this.applyConfig(config);
39207
39208     var mgr = config.mgr;
39209     var pos = config.region;
39210     config.skipConfig = true;
39211     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39212     
39213     if (mgr.el) {
39214         this.onRender(mgr.el);   
39215     }
39216      
39217     this.visible = true;
39218     this.collapsed = false;
39219     this.unrendered_panels = [];
39220 };
39221
39222 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39223
39224     position: '', // set by wrapper (eg. north/south etc..)
39225     unrendered_panels : null,  // unrendered panels.
39226     
39227     tabPosition : false,
39228     
39229     mgr: false, // points to 'Border'
39230     
39231     
39232     createBody : function(){
39233         /** This region's body element 
39234         * @type Roo.Element */
39235         this.bodyEl = this.el.createChild({
39236                 tag: "div",
39237                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39238         });
39239     },
39240
39241     onRender: function(ctr, pos)
39242     {
39243         var dh = Roo.DomHelper;
39244         /** This region's container element 
39245         * @type Roo.Element */
39246         this.el = dh.append(ctr.dom, {
39247                 tag: "div",
39248                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39249             }, true);
39250         /** This region's title element 
39251         * @type Roo.Element */
39252     
39253         this.titleEl = dh.append(this.el.dom,  {
39254                 tag: "div",
39255                 unselectable: "on",
39256                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39257                 children:[
39258                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39259                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39260                 ]
39261             }, true);
39262         
39263         this.titleEl.enableDisplayMode();
39264         /** This region's title text element 
39265         * @type HTMLElement */
39266         this.titleTextEl = this.titleEl.dom.firstChild;
39267         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39268         /*
39269         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39270         this.closeBtn.enableDisplayMode();
39271         this.closeBtn.on("click", this.closeClicked, this);
39272         this.closeBtn.hide();
39273     */
39274         this.createBody(this.config);
39275         if(this.config.hideWhenEmpty){
39276             this.hide();
39277             this.on("paneladded", this.validateVisibility, this);
39278             this.on("panelremoved", this.validateVisibility, this);
39279         }
39280         if(this.autoScroll){
39281             this.bodyEl.setStyle("overflow", "auto");
39282         }else{
39283             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39284         }
39285         //if(c.titlebar !== false){
39286             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39287                 this.titleEl.hide();
39288             }else{
39289                 this.titleEl.show();
39290                 if(this.config.title){
39291                     this.titleTextEl.innerHTML = this.config.title;
39292                 }
39293             }
39294         //}
39295         if(this.config.collapsed){
39296             this.collapse(true);
39297         }
39298         if(this.config.hidden){
39299             this.hide();
39300         }
39301         
39302         if (this.unrendered_panels && this.unrendered_panels.length) {
39303             for (var i =0;i< this.unrendered_panels.length; i++) {
39304                 this.add(this.unrendered_panels[i]);
39305             }
39306             this.unrendered_panels = null;
39307             
39308         }
39309         
39310     },
39311     
39312     applyConfig : function(c)
39313     {
39314         /*
39315          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39316             var dh = Roo.DomHelper;
39317             if(c.titlebar !== false){
39318                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39319                 this.collapseBtn.on("click", this.collapse, this);
39320                 this.collapseBtn.enableDisplayMode();
39321                 /*
39322                 if(c.showPin === true || this.showPin){
39323                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39324                     this.stickBtn.enableDisplayMode();
39325                     this.stickBtn.on("click", this.expand, this);
39326                     this.stickBtn.hide();
39327                 }
39328                 
39329             }
39330             */
39331             /** This region's collapsed element
39332             * @type Roo.Element */
39333             /*
39334              *
39335             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39336                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39337             ]}, true);
39338             
39339             if(c.floatable !== false){
39340                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39341                this.collapsedEl.on("click", this.collapseClick, this);
39342             }
39343
39344             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39345                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39346                    id: "message", unselectable: "on", style:{"float":"left"}});
39347                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39348              }
39349             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39350             this.expandBtn.on("click", this.expand, this);
39351             
39352         }
39353         
39354         if(this.collapseBtn){
39355             this.collapseBtn.setVisible(c.collapsible == true);
39356         }
39357         
39358         this.cmargins = c.cmargins || this.cmargins ||
39359                          (this.position == "west" || this.position == "east" ?
39360                              {top: 0, left: 2, right:2, bottom: 0} :
39361                              {top: 2, left: 0, right:0, bottom: 2});
39362         */
39363         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39364         
39365         
39366         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39367         
39368         this.autoScroll = c.autoScroll || false;
39369         
39370         
39371        
39372         
39373         this.duration = c.duration || .30;
39374         this.slideDuration = c.slideDuration || .45;
39375         this.config = c;
39376        
39377     },
39378     /**
39379      * Returns true if this region is currently visible.
39380      * @return {Boolean}
39381      */
39382     isVisible : function(){
39383         return this.visible;
39384     },
39385
39386     /**
39387      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39388      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39389      */
39390     //setCollapsedTitle : function(title){
39391     //    title = title || "&#160;";
39392      //   if(this.collapsedTitleTextEl){
39393       //      this.collapsedTitleTextEl.innerHTML = title;
39394        // }
39395     //},
39396
39397     getBox : function(){
39398         var b;
39399       //  if(!this.collapsed){
39400             b = this.el.getBox(false, true);
39401        // }else{
39402           //  b = this.collapsedEl.getBox(false, true);
39403         //}
39404         return b;
39405     },
39406
39407     getMargins : function(){
39408         return this.margins;
39409         //return this.collapsed ? this.cmargins : this.margins;
39410     },
39411 /*
39412     highlight : function(){
39413         this.el.addClass("x-layout-panel-dragover");
39414     },
39415
39416     unhighlight : function(){
39417         this.el.removeClass("x-layout-panel-dragover");
39418     },
39419 */
39420     updateBox : function(box)
39421     {
39422         if (!this.bodyEl) {
39423             return; // not rendered yet..
39424         }
39425         
39426         this.box = box;
39427         if(!this.collapsed){
39428             this.el.dom.style.left = box.x + "px";
39429             this.el.dom.style.top = box.y + "px";
39430             this.updateBody(box.width, box.height);
39431         }else{
39432             this.collapsedEl.dom.style.left = box.x + "px";
39433             this.collapsedEl.dom.style.top = box.y + "px";
39434             this.collapsedEl.setSize(box.width, box.height);
39435         }
39436         if(this.tabs){
39437             this.tabs.autoSizeTabs();
39438         }
39439     },
39440
39441     updateBody : function(w, h)
39442     {
39443         if(w !== null){
39444             this.el.setWidth(w);
39445             w -= this.el.getBorderWidth("rl");
39446             if(this.config.adjustments){
39447                 w += this.config.adjustments[0];
39448             }
39449         }
39450         if(h !== null && h > 0){
39451             this.el.setHeight(h);
39452             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39453             h -= this.el.getBorderWidth("tb");
39454             if(this.config.adjustments){
39455                 h += this.config.adjustments[1];
39456             }
39457             this.bodyEl.setHeight(h);
39458             if(this.tabs){
39459                 h = this.tabs.syncHeight(h);
39460             }
39461         }
39462         if(this.panelSize){
39463             w = w !== null ? w : this.panelSize.width;
39464             h = h !== null ? h : this.panelSize.height;
39465         }
39466         if(this.activePanel){
39467             var el = this.activePanel.getEl();
39468             w = w !== null ? w : el.getWidth();
39469             h = h !== null ? h : el.getHeight();
39470             this.panelSize = {width: w, height: h};
39471             this.activePanel.setSize(w, h);
39472         }
39473         if(Roo.isIE && this.tabs){
39474             this.tabs.el.repaint();
39475         }
39476     },
39477
39478     /**
39479      * Returns the container element for this region.
39480      * @return {Roo.Element}
39481      */
39482     getEl : function(){
39483         return this.el;
39484     },
39485
39486     /**
39487      * Hides this region.
39488      */
39489     hide : function(){
39490         //if(!this.collapsed){
39491             this.el.dom.style.left = "-2000px";
39492             this.el.hide();
39493         //}else{
39494          //   this.collapsedEl.dom.style.left = "-2000px";
39495          //   this.collapsedEl.hide();
39496        // }
39497         this.visible = false;
39498         this.fireEvent("visibilitychange", this, false);
39499     },
39500
39501     /**
39502      * Shows this region if it was previously hidden.
39503      */
39504     show : function(){
39505         //if(!this.collapsed){
39506             this.el.show();
39507         //}else{
39508         //    this.collapsedEl.show();
39509        // }
39510         this.visible = true;
39511         this.fireEvent("visibilitychange", this, true);
39512     },
39513 /*
39514     closeClicked : function(){
39515         if(this.activePanel){
39516             this.remove(this.activePanel);
39517         }
39518     },
39519
39520     collapseClick : function(e){
39521         if(this.isSlid){
39522            e.stopPropagation();
39523            this.slideIn();
39524         }else{
39525            e.stopPropagation();
39526            this.slideOut();
39527         }
39528     },
39529 */
39530     /**
39531      * Collapses this region.
39532      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39533      */
39534     /*
39535     collapse : function(skipAnim, skipCheck = false){
39536         if(this.collapsed) {
39537             return;
39538         }
39539         
39540         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39541             
39542             this.collapsed = true;
39543             if(this.split){
39544                 this.split.el.hide();
39545             }
39546             if(this.config.animate && skipAnim !== true){
39547                 this.fireEvent("invalidated", this);
39548                 this.animateCollapse();
39549             }else{
39550                 this.el.setLocation(-20000,-20000);
39551                 this.el.hide();
39552                 this.collapsedEl.show();
39553                 this.fireEvent("collapsed", this);
39554                 this.fireEvent("invalidated", this);
39555             }
39556         }
39557         
39558     },
39559 */
39560     animateCollapse : function(){
39561         // overridden
39562     },
39563
39564     /**
39565      * Expands this region if it was previously collapsed.
39566      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39567      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39568      */
39569     /*
39570     expand : function(e, skipAnim){
39571         if(e) {
39572             e.stopPropagation();
39573         }
39574         if(!this.collapsed || this.el.hasActiveFx()) {
39575             return;
39576         }
39577         if(this.isSlid){
39578             this.afterSlideIn();
39579             skipAnim = true;
39580         }
39581         this.collapsed = false;
39582         if(this.config.animate && skipAnim !== true){
39583             this.animateExpand();
39584         }else{
39585             this.el.show();
39586             if(this.split){
39587                 this.split.el.show();
39588             }
39589             this.collapsedEl.setLocation(-2000,-2000);
39590             this.collapsedEl.hide();
39591             this.fireEvent("invalidated", this);
39592             this.fireEvent("expanded", this);
39593         }
39594     },
39595 */
39596     animateExpand : function(){
39597         // overridden
39598     },
39599
39600     initTabs : function()
39601     {
39602         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39603         
39604         var ts = new Roo.bootstrap.panel.Tabs({
39605             el: this.bodyEl.dom,
39606             region : this,
39607             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39608             disableTooltips: this.config.disableTabTips,
39609             toolbar : this.config.toolbar
39610         });
39611         
39612         if(this.config.hideTabs){
39613             ts.stripWrap.setDisplayed(false);
39614         }
39615         this.tabs = ts;
39616         ts.resizeTabs = this.config.resizeTabs === true;
39617         ts.minTabWidth = this.config.minTabWidth || 40;
39618         ts.maxTabWidth = this.config.maxTabWidth || 250;
39619         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39620         ts.monitorResize = false;
39621         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39622         ts.bodyEl.addClass('roo-layout-tabs-body');
39623         this.panels.each(this.initPanelAsTab, this);
39624     },
39625
39626     initPanelAsTab : function(panel){
39627         var ti = this.tabs.addTab(
39628             panel.getEl().id,
39629             panel.getTitle(),
39630             null,
39631             this.config.closeOnTab && panel.isClosable(),
39632             panel.tpl
39633         );
39634         if(panel.tabTip !== undefined){
39635             ti.setTooltip(panel.tabTip);
39636         }
39637         ti.on("activate", function(){
39638               this.setActivePanel(panel);
39639         }, this);
39640         
39641         if(this.config.closeOnTab){
39642             ti.on("beforeclose", function(t, e){
39643                 e.cancel = true;
39644                 this.remove(panel);
39645             }, this);
39646         }
39647         
39648         panel.tabItem = ti;
39649         
39650         return ti;
39651     },
39652
39653     updatePanelTitle : function(panel, title)
39654     {
39655         if(this.activePanel == panel){
39656             this.updateTitle(title);
39657         }
39658         if(this.tabs){
39659             var ti = this.tabs.getTab(panel.getEl().id);
39660             ti.setText(title);
39661             if(panel.tabTip !== undefined){
39662                 ti.setTooltip(panel.tabTip);
39663             }
39664         }
39665     },
39666
39667     updateTitle : function(title){
39668         if(this.titleTextEl && !this.config.title){
39669             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39670         }
39671     },
39672
39673     setActivePanel : function(panel)
39674     {
39675         panel = this.getPanel(panel);
39676         if(this.activePanel && this.activePanel != panel){
39677             if(this.activePanel.setActiveState(false) === false){
39678                 return;
39679             }
39680         }
39681         this.activePanel = panel;
39682         panel.setActiveState(true);
39683         if(this.panelSize){
39684             panel.setSize(this.panelSize.width, this.panelSize.height);
39685         }
39686         if(this.closeBtn){
39687             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39688         }
39689         this.updateTitle(panel.getTitle());
39690         if(this.tabs){
39691             this.fireEvent("invalidated", this);
39692         }
39693         this.fireEvent("panelactivated", this, panel);
39694     },
39695
39696     /**
39697      * Shows the specified panel.
39698      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39699      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39700      */
39701     showPanel : function(panel)
39702     {
39703         panel = this.getPanel(panel);
39704         if(panel){
39705             if(this.tabs){
39706                 var tab = this.tabs.getTab(panel.getEl().id);
39707                 if(tab.isHidden()){
39708                     this.tabs.unhideTab(tab.id);
39709                 }
39710                 tab.activate();
39711             }else{
39712                 this.setActivePanel(panel);
39713             }
39714         }
39715         return panel;
39716     },
39717
39718     /**
39719      * Get the active panel for this region.
39720      * @return {Roo.ContentPanel} The active panel or null
39721      */
39722     getActivePanel : function(){
39723         return this.activePanel;
39724     },
39725
39726     validateVisibility : function(){
39727         if(this.panels.getCount() < 1){
39728             this.updateTitle("&#160;");
39729             this.closeBtn.hide();
39730             this.hide();
39731         }else{
39732             if(!this.isVisible()){
39733                 this.show();
39734             }
39735         }
39736     },
39737
39738     /**
39739      * Adds the passed ContentPanel(s) to this region.
39740      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39741      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39742      */
39743     add : function(panel)
39744     {
39745         if(arguments.length > 1){
39746             for(var i = 0, len = arguments.length; i < len; i++) {
39747                 this.add(arguments[i]);
39748             }
39749             return null;
39750         }
39751         
39752         // if we have not been rendered yet, then we can not really do much of this..
39753         if (!this.bodyEl) {
39754             this.unrendered_panels.push(panel);
39755             return panel;
39756         }
39757         
39758         
39759         
39760         
39761         if(this.hasPanel(panel)){
39762             this.showPanel(panel);
39763             return panel;
39764         }
39765         panel.setRegion(this);
39766         this.panels.add(panel);
39767        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39768             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39769             // and hide them... ???
39770             this.bodyEl.dom.appendChild(panel.getEl().dom);
39771             if(panel.background !== true){
39772                 this.setActivePanel(panel);
39773             }
39774             this.fireEvent("paneladded", this, panel);
39775             return panel;
39776         }
39777         */
39778         if(!this.tabs){
39779             this.initTabs();
39780         }else{
39781             this.initPanelAsTab(panel);
39782         }
39783         
39784         
39785         if(panel.background !== true){
39786             this.tabs.activate(panel.getEl().id);
39787         }
39788         this.fireEvent("paneladded", this, panel);
39789         return panel;
39790     },
39791
39792     /**
39793      * Hides the tab for the specified panel.
39794      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39795      */
39796     hidePanel : function(panel){
39797         if(this.tabs && (panel = this.getPanel(panel))){
39798             this.tabs.hideTab(panel.getEl().id);
39799         }
39800     },
39801
39802     /**
39803      * Unhides the tab for a previously hidden panel.
39804      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39805      */
39806     unhidePanel : function(panel){
39807         if(this.tabs && (panel = this.getPanel(panel))){
39808             this.tabs.unhideTab(panel.getEl().id);
39809         }
39810     },
39811
39812     clearPanels : function(){
39813         while(this.panels.getCount() > 0){
39814              this.remove(this.panels.first());
39815         }
39816     },
39817
39818     /**
39819      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39820      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39821      * @param {Boolean} preservePanel Overrides the config preservePanel option
39822      * @return {Roo.ContentPanel} The panel that was removed
39823      */
39824     remove : function(panel, preservePanel)
39825     {
39826         panel = this.getPanel(panel);
39827         if(!panel){
39828             return null;
39829         }
39830         var e = {};
39831         this.fireEvent("beforeremove", this, panel, e);
39832         if(e.cancel === true){
39833             return null;
39834         }
39835         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39836         var panelId = panel.getId();
39837         this.panels.removeKey(panelId);
39838         if(preservePanel){
39839             document.body.appendChild(panel.getEl().dom);
39840         }
39841         if(this.tabs){
39842             this.tabs.removeTab(panel.getEl().id);
39843         }else if (!preservePanel){
39844             this.bodyEl.dom.removeChild(panel.getEl().dom);
39845         }
39846         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39847             var p = this.panels.first();
39848             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39849             tempEl.appendChild(p.getEl().dom);
39850             this.bodyEl.update("");
39851             this.bodyEl.dom.appendChild(p.getEl().dom);
39852             tempEl = null;
39853             this.updateTitle(p.getTitle());
39854             this.tabs = null;
39855             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39856             this.setActivePanel(p);
39857         }
39858         panel.setRegion(null);
39859         if(this.activePanel == panel){
39860             this.activePanel = null;
39861         }
39862         if(this.config.autoDestroy !== false && preservePanel !== true){
39863             try{panel.destroy();}catch(e){}
39864         }
39865         this.fireEvent("panelremoved", this, panel);
39866         return panel;
39867     },
39868
39869     /**
39870      * Returns the TabPanel component used by this region
39871      * @return {Roo.TabPanel}
39872      */
39873     getTabs : function(){
39874         return this.tabs;
39875     },
39876
39877     createTool : function(parentEl, className){
39878         var btn = Roo.DomHelper.append(parentEl, {
39879             tag: "div",
39880             cls: "x-layout-tools-button",
39881             children: [ {
39882                 tag: "div",
39883                 cls: "roo-layout-tools-button-inner " + className,
39884                 html: "&#160;"
39885             }]
39886         }, true);
39887         btn.addClassOnOver("roo-layout-tools-button-over");
39888         return btn;
39889     }
39890 });/*
39891  * Based on:
39892  * Ext JS Library 1.1.1
39893  * Copyright(c) 2006-2007, Ext JS, LLC.
39894  *
39895  * Originally Released Under LGPL - original licence link has changed is not relivant.
39896  *
39897  * Fork - LGPL
39898  * <script type="text/javascript">
39899  */
39900  
39901
39902
39903 /**
39904  * @class Roo.SplitLayoutRegion
39905  * @extends Roo.LayoutRegion
39906  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39907  */
39908 Roo.bootstrap.layout.Split = function(config){
39909     this.cursor = config.cursor;
39910     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39911 };
39912
39913 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39914 {
39915     splitTip : "Drag to resize.",
39916     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39917     useSplitTips : false,
39918
39919     applyConfig : function(config){
39920         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39921     },
39922     
39923     onRender : function(ctr,pos) {
39924         
39925         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39926         if(!this.config.split){
39927             return;
39928         }
39929         if(!this.split){
39930             
39931             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39932                             tag: "div",
39933                             id: this.el.id + "-split",
39934                             cls: "roo-layout-split roo-layout-split-"+this.position,
39935                             html: "&#160;"
39936             });
39937             /** The SplitBar for this region 
39938             * @type Roo.SplitBar */
39939             // does not exist yet...
39940             Roo.log([this.position, this.orientation]);
39941             
39942             this.split = new Roo.bootstrap.SplitBar({
39943                 dragElement : splitEl,
39944                 resizingElement: this.el,
39945                 orientation : this.orientation
39946             });
39947             
39948             this.split.on("moved", this.onSplitMove, this);
39949             this.split.useShim = this.config.useShim === true;
39950             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39951             if(this.useSplitTips){
39952                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39953             }
39954             //if(config.collapsible){
39955             //    this.split.el.on("dblclick", this.collapse,  this);
39956             //}
39957         }
39958         if(typeof this.config.minSize != "undefined"){
39959             this.split.minSize = this.config.minSize;
39960         }
39961         if(typeof this.config.maxSize != "undefined"){
39962             this.split.maxSize = this.config.maxSize;
39963         }
39964         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39965             this.hideSplitter();
39966         }
39967         
39968     },
39969
39970     getHMaxSize : function(){
39971          var cmax = this.config.maxSize || 10000;
39972          var center = this.mgr.getRegion("center");
39973          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39974     },
39975
39976     getVMaxSize : function(){
39977          var cmax = this.config.maxSize || 10000;
39978          var center = this.mgr.getRegion("center");
39979          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39980     },
39981
39982     onSplitMove : function(split, newSize){
39983         this.fireEvent("resized", this, newSize);
39984     },
39985     
39986     /** 
39987      * Returns the {@link Roo.SplitBar} for this region.
39988      * @return {Roo.SplitBar}
39989      */
39990     getSplitBar : function(){
39991         return this.split;
39992     },
39993     
39994     hide : function(){
39995         this.hideSplitter();
39996         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39997     },
39998
39999     hideSplitter : function(){
40000         if(this.split){
40001             this.split.el.setLocation(-2000,-2000);
40002             this.split.el.hide();
40003         }
40004     },
40005
40006     show : function(){
40007         if(this.split){
40008             this.split.el.show();
40009         }
40010         Roo.bootstrap.layout.Split.superclass.show.call(this);
40011     },
40012     
40013     beforeSlide: function(){
40014         if(Roo.isGecko){// firefox overflow auto bug workaround
40015             this.bodyEl.clip();
40016             if(this.tabs) {
40017                 this.tabs.bodyEl.clip();
40018             }
40019             if(this.activePanel){
40020                 this.activePanel.getEl().clip();
40021                 
40022                 if(this.activePanel.beforeSlide){
40023                     this.activePanel.beforeSlide();
40024                 }
40025             }
40026         }
40027     },
40028     
40029     afterSlide : function(){
40030         if(Roo.isGecko){// firefox overflow auto bug workaround
40031             this.bodyEl.unclip();
40032             if(this.tabs) {
40033                 this.tabs.bodyEl.unclip();
40034             }
40035             if(this.activePanel){
40036                 this.activePanel.getEl().unclip();
40037                 if(this.activePanel.afterSlide){
40038                     this.activePanel.afterSlide();
40039                 }
40040             }
40041         }
40042     },
40043
40044     initAutoHide : function(){
40045         if(this.autoHide !== false){
40046             if(!this.autoHideHd){
40047                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40048                 this.autoHideHd = {
40049                     "mouseout": function(e){
40050                         if(!e.within(this.el, true)){
40051                             st.delay(500);
40052                         }
40053                     },
40054                     "mouseover" : function(e){
40055                         st.cancel();
40056                     },
40057                     scope : this
40058                 };
40059             }
40060             this.el.on(this.autoHideHd);
40061         }
40062     },
40063
40064     clearAutoHide : function(){
40065         if(this.autoHide !== false){
40066             this.el.un("mouseout", this.autoHideHd.mouseout);
40067             this.el.un("mouseover", this.autoHideHd.mouseover);
40068         }
40069     },
40070
40071     clearMonitor : function(){
40072         Roo.get(document).un("click", this.slideInIf, this);
40073     },
40074
40075     // these names are backwards but not changed for compat
40076     slideOut : function(){
40077         if(this.isSlid || this.el.hasActiveFx()){
40078             return;
40079         }
40080         this.isSlid = true;
40081         if(this.collapseBtn){
40082             this.collapseBtn.hide();
40083         }
40084         this.closeBtnState = this.closeBtn.getStyle('display');
40085         this.closeBtn.hide();
40086         if(this.stickBtn){
40087             this.stickBtn.show();
40088         }
40089         this.el.show();
40090         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40091         this.beforeSlide();
40092         this.el.setStyle("z-index", 10001);
40093         this.el.slideIn(this.getSlideAnchor(), {
40094             callback: function(){
40095                 this.afterSlide();
40096                 this.initAutoHide();
40097                 Roo.get(document).on("click", this.slideInIf, this);
40098                 this.fireEvent("slideshow", this);
40099             },
40100             scope: this,
40101             block: true
40102         });
40103     },
40104
40105     afterSlideIn : function(){
40106         this.clearAutoHide();
40107         this.isSlid = false;
40108         this.clearMonitor();
40109         this.el.setStyle("z-index", "");
40110         if(this.collapseBtn){
40111             this.collapseBtn.show();
40112         }
40113         this.closeBtn.setStyle('display', this.closeBtnState);
40114         if(this.stickBtn){
40115             this.stickBtn.hide();
40116         }
40117         this.fireEvent("slidehide", this);
40118     },
40119
40120     slideIn : function(cb){
40121         if(!this.isSlid || this.el.hasActiveFx()){
40122             Roo.callback(cb);
40123             return;
40124         }
40125         this.isSlid = false;
40126         this.beforeSlide();
40127         this.el.slideOut(this.getSlideAnchor(), {
40128             callback: function(){
40129                 this.el.setLeftTop(-10000, -10000);
40130                 this.afterSlide();
40131                 this.afterSlideIn();
40132                 Roo.callback(cb);
40133             },
40134             scope: this,
40135             block: true
40136         });
40137     },
40138     
40139     slideInIf : function(e){
40140         if(!e.within(this.el)){
40141             this.slideIn();
40142         }
40143     },
40144
40145     animateCollapse : function(){
40146         this.beforeSlide();
40147         this.el.setStyle("z-index", 20000);
40148         var anchor = this.getSlideAnchor();
40149         this.el.slideOut(anchor, {
40150             callback : function(){
40151                 this.el.setStyle("z-index", "");
40152                 this.collapsedEl.slideIn(anchor, {duration:.3});
40153                 this.afterSlide();
40154                 this.el.setLocation(-10000,-10000);
40155                 this.el.hide();
40156                 this.fireEvent("collapsed", this);
40157             },
40158             scope: this,
40159             block: true
40160         });
40161     },
40162
40163     animateExpand : function(){
40164         this.beforeSlide();
40165         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40166         this.el.setStyle("z-index", 20000);
40167         this.collapsedEl.hide({
40168             duration:.1
40169         });
40170         this.el.slideIn(this.getSlideAnchor(), {
40171             callback : function(){
40172                 this.el.setStyle("z-index", "");
40173                 this.afterSlide();
40174                 if(this.split){
40175                     this.split.el.show();
40176                 }
40177                 this.fireEvent("invalidated", this);
40178                 this.fireEvent("expanded", this);
40179             },
40180             scope: this,
40181             block: true
40182         });
40183     },
40184
40185     anchors : {
40186         "west" : "left",
40187         "east" : "right",
40188         "north" : "top",
40189         "south" : "bottom"
40190     },
40191
40192     sanchors : {
40193         "west" : "l",
40194         "east" : "r",
40195         "north" : "t",
40196         "south" : "b"
40197     },
40198
40199     canchors : {
40200         "west" : "tl-tr",
40201         "east" : "tr-tl",
40202         "north" : "tl-bl",
40203         "south" : "bl-tl"
40204     },
40205
40206     getAnchor : function(){
40207         return this.anchors[this.position];
40208     },
40209
40210     getCollapseAnchor : function(){
40211         return this.canchors[this.position];
40212     },
40213
40214     getSlideAnchor : function(){
40215         return this.sanchors[this.position];
40216     },
40217
40218     getAlignAdj : function(){
40219         var cm = this.cmargins;
40220         switch(this.position){
40221             case "west":
40222                 return [0, 0];
40223             break;
40224             case "east":
40225                 return [0, 0];
40226             break;
40227             case "north":
40228                 return [0, 0];
40229             break;
40230             case "south":
40231                 return [0, 0];
40232             break;
40233         }
40234     },
40235
40236     getExpandAdj : function(){
40237         var c = this.collapsedEl, cm = this.cmargins;
40238         switch(this.position){
40239             case "west":
40240                 return [-(cm.right+c.getWidth()+cm.left), 0];
40241             break;
40242             case "east":
40243                 return [cm.right+c.getWidth()+cm.left, 0];
40244             break;
40245             case "north":
40246                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40247             break;
40248             case "south":
40249                 return [0, cm.top+cm.bottom+c.getHeight()];
40250             break;
40251         }
40252     }
40253 });/*
40254  * Based on:
40255  * Ext JS Library 1.1.1
40256  * Copyright(c) 2006-2007, Ext JS, LLC.
40257  *
40258  * Originally Released Under LGPL - original licence link has changed is not relivant.
40259  *
40260  * Fork - LGPL
40261  * <script type="text/javascript">
40262  */
40263 /*
40264  * These classes are private internal classes
40265  */
40266 Roo.bootstrap.layout.Center = function(config){
40267     config.region = "center";
40268     Roo.bootstrap.layout.Region.call(this, config);
40269     this.visible = true;
40270     this.minWidth = config.minWidth || 20;
40271     this.minHeight = config.minHeight || 20;
40272 };
40273
40274 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40275     hide : function(){
40276         // center panel can't be hidden
40277     },
40278     
40279     show : function(){
40280         // center panel can't be hidden
40281     },
40282     
40283     getMinWidth: function(){
40284         return this.minWidth;
40285     },
40286     
40287     getMinHeight: function(){
40288         return this.minHeight;
40289     }
40290 });
40291
40292
40293
40294
40295  
40296
40297
40298
40299
40300
40301
40302 Roo.bootstrap.layout.North = function(config)
40303 {
40304     config.region = 'north';
40305     config.cursor = 'n-resize';
40306     
40307     Roo.bootstrap.layout.Split.call(this, config);
40308     
40309     
40310     if(this.split){
40311         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40312         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40313         this.split.el.addClass("roo-layout-split-v");
40314     }
40315     //var size = config.initialSize || config.height;
40316     //if(this.el && typeof size != "undefined"){
40317     //    this.el.setHeight(size);
40318     //}
40319 };
40320 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40321 {
40322     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40323      
40324      
40325     onRender : function(ctr, pos)
40326     {
40327         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40328         var size = this.config.initialSize || this.config.height;
40329         if(this.el && typeof size != "undefined"){
40330             this.el.setHeight(size);
40331         }
40332     
40333     },
40334     
40335     getBox : function(){
40336         if(this.collapsed){
40337             return this.collapsedEl.getBox();
40338         }
40339         var box = this.el.getBox();
40340         if(this.split){
40341             box.height += this.split.el.getHeight();
40342         }
40343         return box;
40344     },
40345     
40346     updateBox : function(box){
40347         if(this.split && !this.collapsed){
40348             box.height -= this.split.el.getHeight();
40349             this.split.el.setLeft(box.x);
40350             this.split.el.setTop(box.y+box.height);
40351             this.split.el.setWidth(box.width);
40352         }
40353         if(this.collapsed){
40354             this.updateBody(box.width, null);
40355         }
40356         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40357     }
40358 });
40359
40360
40361
40362
40363
40364 Roo.bootstrap.layout.South = function(config){
40365     config.region = 'south';
40366     config.cursor = 's-resize';
40367     Roo.bootstrap.layout.Split.call(this, config);
40368     if(this.split){
40369         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40370         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40371         this.split.el.addClass("roo-layout-split-v");
40372     }
40373     
40374 };
40375
40376 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40377     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40378     
40379     onRender : function(ctr, pos)
40380     {
40381         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40382         var size = this.config.initialSize || this.config.height;
40383         if(this.el && typeof size != "undefined"){
40384             this.el.setHeight(size);
40385         }
40386     
40387     },
40388     
40389     getBox : function(){
40390         if(this.collapsed){
40391             return this.collapsedEl.getBox();
40392         }
40393         var box = this.el.getBox();
40394         if(this.split){
40395             var sh = this.split.el.getHeight();
40396             box.height += sh;
40397             box.y -= sh;
40398         }
40399         return box;
40400     },
40401     
40402     updateBox : function(box){
40403         if(this.split && !this.collapsed){
40404             var sh = this.split.el.getHeight();
40405             box.height -= sh;
40406             box.y += sh;
40407             this.split.el.setLeft(box.x);
40408             this.split.el.setTop(box.y-sh);
40409             this.split.el.setWidth(box.width);
40410         }
40411         if(this.collapsed){
40412             this.updateBody(box.width, null);
40413         }
40414         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40415     }
40416 });
40417
40418 Roo.bootstrap.layout.East = function(config){
40419     config.region = "east";
40420     config.cursor = "e-resize";
40421     Roo.bootstrap.layout.Split.call(this, config);
40422     if(this.split){
40423         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40424         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40425         this.split.el.addClass("roo-layout-split-h");
40426     }
40427     
40428 };
40429 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40430     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40431     
40432     onRender : function(ctr, pos)
40433     {
40434         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40435         var size = this.config.initialSize || this.config.width;
40436         if(this.el && typeof size != "undefined"){
40437             this.el.setWidth(size);
40438         }
40439     
40440     },
40441     
40442     getBox : function(){
40443         if(this.collapsed){
40444             return this.collapsedEl.getBox();
40445         }
40446         var box = this.el.getBox();
40447         if(this.split){
40448             var sw = this.split.el.getWidth();
40449             box.width += sw;
40450             box.x -= sw;
40451         }
40452         return box;
40453     },
40454
40455     updateBox : function(box){
40456         if(this.split && !this.collapsed){
40457             var sw = this.split.el.getWidth();
40458             box.width -= sw;
40459             this.split.el.setLeft(box.x);
40460             this.split.el.setTop(box.y);
40461             this.split.el.setHeight(box.height);
40462             box.x += sw;
40463         }
40464         if(this.collapsed){
40465             this.updateBody(null, box.height);
40466         }
40467         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40468     }
40469 });
40470
40471 Roo.bootstrap.layout.West = function(config){
40472     config.region = "west";
40473     config.cursor = "w-resize";
40474     
40475     Roo.bootstrap.layout.Split.call(this, config);
40476     if(this.split){
40477         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40478         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40479         this.split.el.addClass("roo-layout-split-h");
40480     }
40481     
40482 };
40483 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40484     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40485     
40486     onRender: function(ctr, pos)
40487     {
40488         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40489         var size = this.config.initialSize || this.config.width;
40490         if(typeof size != "undefined"){
40491             this.el.setWidth(size);
40492         }
40493     },
40494     
40495     getBox : function(){
40496         if(this.collapsed){
40497             return this.collapsedEl.getBox();
40498         }
40499         var box = this.el.getBox();
40500         if (box.width == 0) {
40501             box.width = this.config.width; // kludge?
40502         }
40503         if(this.split){
40504             box.width += this.split.el.getWidth();
40505         }
40506         return box;
40507     },
40508     
40509     updateBox : function(box){
40510         if(this.split && !this.collapsed){
40511             var sw = this.split.el.getWidth();
40512             box.width -= sw;
40513             this.split.el.setLeft(box.x+box.width);
40514             this.split.el.setTop(box.y);
40515             this.split.el.setHeight(box.height);
40516         }
40517         if(this.collapsed){
40518             this.updateBody(null, box.height);
40519         }
40520         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40521     }
40522 });Roo.namespace("Roo.bootstrap.panel");/*
40523  * Based on:
40524  * Ext JS Library 1.1.1
40525  * Copyright(c) 2006-2007, Ext JS, LLC.
40526  *
40527  * Originally Released Under LGPL - original licence link has changed is not relivant.
40528  *
40529  * Fork - LGPL
40530  * <script type="text/javascript">
40531  */
40532 /**
40533  * @class Roo.ContentPanel
40534  * @extends Roo.util.Observable
40535  * A basic ContentPanel element.
40536  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40537  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40538  * @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
40539  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40540  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40541  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40542  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40543  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40544  * @cfg {String} title          The title for this panel
40545  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40546  * @cfg {String} url            Calls {@link #setUrl} with this value
40547  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40548  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40549  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40550  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40551  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40552  * @cfg {Boolean} badges render the badges
40553  * @cfg {String} cls  extra classes to use  
40554  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40555
40556  * @constructor
40557  * Create a new ContentPanel.
40558  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40559  * @param {String/Object} config A string to set only the title or a config object
40560  * @param {String} content (optional) Set the HTML content for this panel
40561  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40562  */
40563 Roo.bootstrap.panel.Content = function( config){
40564     
40565     this.tpl = config.tpl || false;
40566     
40567     var el = config.el;
40568     var content = config.content;
40569
40570     if(config.autoCreate){ // xtype is available if this is called from factory
40571         el = Roo.id();
40572     }
40573     this.el = Roo.get(el);
40574     if(!this.el && config && config.autoCreate){
40575         if(typeof config.autoCreate == "object"){
40576             if(!config.autoCreate.id){
40577                 config.autoCreate.id = config.id||el;
40578             }
40579             this.el = Roo.DomHelper.append(document.body,
40580                         config.autoCreate, true);
40581         }else{
40582             var elcfg =  {
40583                 tag: "div",
40584                 cls: (config.cls || '') +
40585                     (config.background ? ' bg-' + config.background : '') +
40586                     " roo-layout-inactive-content",
40587                 id: config.id||el
40588             };
40589             if (config.iframe) {
40590                 elcfg.cn = [
40591                     {
40592                         tag : 'iframe',
40593                         style : 'border: 0px',
40594                         src : 'about:blank'
40595                     }
40596                 ];
40597             }
40598               
40599             if (config.html) {
40600                 elcfg.html = config.html;
40601                 
40602             }
40603                         
40604             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40605             if (config.iframe) {
40606                 this.iframeEl = this.el.select('iframe',true).first();
40607             }
40608             
40609         }
40610     } 
40611     this.closable = false;
40612     this.loaded = false;
40613     this.active = false;
40614    
40615       
40616     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40617         
40618         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40619         
40620         this.wrapEl = this.el; //this.el.wrap();
40621         var ti = [];
40622         if (config.toolbar.items) {
40623             ti = config.toolbar.items ;
40624             delete config.toolbar.items ;
40625         }
40626         
40627         var nitems = [];
40628         this.toolbar.render(this.wrapEl, 'before');
40629         for(var i =0;i < ti.length;i++) {
40630           //  Roo.log(['add child', items[i]]);
40631             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40632         }
40633         this.toolbar.items = nitems;
40634         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40635         delete config.toolbar;
40636         
40637     }
40638     /*
40639     // xtype created footer. - not sure if will work as we normally have to render first..
40640     if (this.footer && !this.footer.el && this.footer.xtype) {
40641         if (!this.wrapEl) {
40642             this.wrapEl = this.el.wrap();
40643         }
40644     
40645         this.footer.container = this.wrapEl.createChild();
40646          
40647         this.footer = Roo.factory(this.footer, Roo);
40648         
40649     }
40650     */
40651     
40652      if(typeof config == "string"){
40653         this.title = config;
40654     }else{
40655         Roo.apply(this, config);
40656     }
40657     
40658     if(this.resizeEl){
40659         this.resizeEl = Roo.get(this.resizeEl, true);
40660     }else{
40661         this.resizeEl = this.el;
40662     }
40663     // handle view.xtype
40664     
40665  
40666     
40667     
40668     this.addEvents({
40669         /**
40670          * @event activate
40671          * Fires when this panel is activated. 
40672          * @param {Roo.ContentPanel} this
40673          */
40674         "activate" : true,
40675         /**
40676          * @event deactivate
40677          * Fires when this panel is activated. 
40678          * @param {Roo.ContentPanel} this
40679          */
40680         "deactivate" : true,
40681
40682         /**
40683          * @event resize
40684          * Fires when this panel is resized if fitToFrame is true.
40685          * @param {Roo.ContentPanel} this
40686          * @param {Number} width The width after any component adjustments
40687          * @param {Number} height The height after any component adjustments
40688          */
40689         "resize" : true,
40690         
40691          /**
40692          * @event render
40693          * Fires when this tab is created
40694          * @param {Roo.ContentPanel} this
40695          */
40696         "render" : true,
40697         
40698           /**
40699          * @event scroll
40700          * Fires when this content is scrolled
40701          * @param {Roo.ContentPanel} this
40702          * @param {Event} scrollEvent
40703          */
40704         "scroll" : true
40705         
40706         
40707         
40708     });
40709     
40710
40711     
40712     
40713     if(this.autoScroll && !this.iframe){
40714         this.resizeEl.setStyle("overflow", "auto");
40715         this.resizeEl.on('scroll', this.onScroll, this);
40716     } else {
40717         // fix randome scrolling
40718         //this.el.on('scroll', function() {
40719         //    Roo.log('fix random scolling');
40720         //    this.scrollTo('top',0); 
40721         //});
40722     }
40723     content = content || this.content;
40724     if(content){
40725         this.setContent(content);
40726     }
40727     if(config && config.url){
40728         this.setUrl(this.url, this.params, this.loadOnce);
40729     }
40730     
40731     
40732     
40733     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40734     
40735     if (this.view && typeof(this.view.xtype) != 'undefined') {
40736         this.view.el = this.el.appendChild(document.createElement("div"));
40737         this.view = Roo.factory(this.view); 
40738         this.view.render  &&  this.view.render(false, '');  
40739     }
40740     
40741     
40742     this.fireEvent('render', this);
40743 };
40744
40745 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40746     
40747     cls : '',
40748     background : '',
40749     
40750     tabTip : '',
40751     
40752     iframe : false,
40753     iframeEl : false,
40754     
40755     /* Resize Element - use this to work out scroll etc. */
40756     resizeEl : false,
40757     
40758     setRegion : function(region){
40759         this.region = region;
40760         this.setActiveClass(region && !this.background);
40761     },
40762     
40763     
40764     setActiveClass: function(state)
40765     {
40766         if(state){
40767            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40768            this.el.setStyle('position','relative');
40769         }else{
40770            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40771            this.el.setStyle('position', 'absolute');
40772         } 
40773     },
40774     
40775     /**
40776      * Returns the toolbar for this Panel if one was configured. 
40777      * @return {Roo.Toolbar} 
40778      */
40779     getToolbar : function(){
40780         return this.toolbar;
40781     },
40782     
40783     setActiveState : function(active)
40784     {
40785         this.active = active;
40786         this.setActiveClass(active);
40787         if(!active){
40788             if(this.fireEvent("deactivate", this) === false){
40789                 return false;
40790             }
40791             return true;
40792         }
40793         this.fireEvent("activate", this);
40794         return true;
40795     },
40796     /**
40797      * Updates this panel's element (not for iframe)
40798      * @param {String} content The new content
40799      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40800     */
40801     setContent : function(content, loadScripts){
40802         if (this.iframe) {
40803             return;
40804         }
40805         
40806         this.el.update(content, loadScripts);
40807     },
40808
40809     ignoreResize : function(w, h){
40810         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40811             return true;
40812         }else{
40813             this.lastSize = {width: w, height: h};
40814             return false;
40815         }
40816     },
40817     /**
40818      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40819      * @return {Roo.UpdateManager} The UpdateManager
40820      */
40821     getUpdateManager : function(){
40822         if (this.iframe) {
40823             return false;
40824         }
40825         return this.el.getUpdateManager();
40826     },
40827      /**
40828      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40829      * Does not work with IFRAME contents
40830      * @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:
40831 <pre><code>
40832 panel.load({
40833     url: "your-url.php",
40834     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40835     callback: yourFunction,
40836     scope: yourObject, //(optional scope)
40837     discardUrl: false,
40838     nocache: false,
40839     text: "Loading...",
40840     timeout: 30,
40841     scripts: false
40842 });
40843 </code></pre>
40844      
40845      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40846      * 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.
40847      * @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}
40848      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40849      * @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.
40850      * @return {Roo.ContentPanel} this
40851      */
40852     load : function(){
40853         
40854         if (this.iframe) {
40855             return this;
40856         }
40857         
40858         var um = this.el.getUpdateManager();
40859         um.update.apply(um, arguments);
40860         return this;
40861     },
40862
40863
40864     /**
40865      * 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.
40866      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40867      * @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)
40868      * @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)
40869      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40870      */
40871     setUrl : function(url, params, loadOnce){
40872         if (this.iframe) {
40873             this.iframeEl.dom.src = url;
40874             return false;
40875         }
40876         
40877         if(this.refreshDelegate){
40878             this.removeListener("activate", this.refreshDelegate);
40879         }
40880         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40881         this.on("activate", this.refreshDelegate);
40882         return this.el.getUpdateManager();
40883     },
40884     
40885     _handleRefresh : function(url, params, loadOnce){
40886         if(!loadOnce || !this.loaded){
40887             var updater = this.el.getUpdateManager();
40888             updater.update(url, params, this._setLoaded.createDelegate(this));
40889         }
40890     },
40891     
40892     _setLoaded : function(){
40893         this.loaded = true;
40894     }, 
40895     
40896     /**
40897      * Returns this panel's id
40898      * @return {String} 
40899      */
40900     getId : function(){
40901         return this.el.id;
40902     },
40903     
40904     /** 
40905      * Returns this panel's element - used by regiosn to add.
40906      * @return {Roo.Element} 
40907      */
40908     getEl : function(){
40909         return this.wrapEl || this.el;
40910     },
40911     
40912    
40913     
40914     adjustForComponents : function(width, height)
40915     {
40916         //Roo.log('adjustForComponents ');
40917         if(this.resizeEl != this.el){
40918             width -= this.el.getFrameWidth('lr');
40919             height -= this.el.getFrameWidth('tb');
40920         }
40921         if(this.toolbar){
40922             var te = this.toolbar.getEl();
40923             te.setWidth(width);
40924             height -= te.getHeight();
40925         }
40926         if(this.footer){
40927             var te = this.footer.getEl();
40928             te.setWidth(width);
40929             height -= te.getHeight();
40930         }
40931         
40932         
40933         if(this.adjustments){
40934             width += this.adjustments[0];
40935             height += this.adjustments[1];
40936         }
40937         return {"width": width, "height": height};
40938     },
40939     
40940     setSize : function(width, height){
40941         if(this.fitToFrame && !this.ignoreResize(width, height)){
40942             if(this.fitContainer && this.resizeEl != this.el){
40943                 this.el.setSize(width, height);
40944             }
40945             var size = this.adjustForComponents(width, height);
40946             if (this.iframe) {
40947                 this.iframeEl.setSize(width,height);
40948             }
40949             
40950             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40951             this.fireEvent('resize', this, size.width, size.height);
40952             
40953             
40954         }
40955     },
40956     
40957     /**
40958      * Returns this panel's title
40959      * @return {String} 
40960      */
40961     getTitle : function(){
40962         
40963         if (typeof(this.title) != 'object') {
40964             return this.title;
40965         }
40966         
40967         var t = '';
40968         for (var k in this.title) {
40969             if (!this.title.hasOwnProperty(k)) {
40970                 continue;
40971             }
40972             
40973             if (k.indexOf('-') >= 0) {
40974                 var s = k.split('-');
40975                 for (var i = 0; i<s.length; i++) {
40976                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40977                 }
40978             } else {
40979                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40980             }
40981         }
40982         return t;
40983     },
40984     
40985     /**
40986      * Set this panel's title
40987      * @param {String} title
40988      */
40989     setTitle : function(title){
40990         this.title = title;
40991         if(this.region){
40992             this.region.updatePanelTitle(this, title);
40993         }
40994     },
40995     
40996     /**
40997      * Returns true is this panel was configured to be closable
40998      * @return {Boolean} 
40999      */
41000     isClosable : function(){
41001         return this.closable;
41002     },
41003     
41004     beforeSlide : function(){
41005         this.el.clip();
41006         this.resizeEl.clip();
41007     },
41008     
41009     afterSlide : function(){
41010         this.el.unclip();
41011         this.resizeEl.unclip();
41012     },
41013     
41014     /**
41015      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41016      *   Will fail silently if the {@link #setUrl} method has not been called.
41017      *   This does not activate the panel, just updates its content.
41018      */
41019     refresh : function(){
41020         if(this.refreshDelegate){
41021            this.loaded = false;
41022            this.refreshDelegate();
41023         }
41024     },
41025     
41026     /**
41027      * Destroys this panel
41028      */
41029     destroy : function(){
41030         this.el.removeAllListeners();
41031         var tempEl = document.createElement("span");
41032         tempEl.appendChild(this.el.dom);
41033         tempEl.innerHTML = "";
41034         this.el.remove();
41035         this.el = null;
41036     },
41037     
41038     /**
41039      * form - if the content panel contains a form - this is a reference to it.
41040      * @type {Roo.form.Form}
41041      */
41042     form : false,
41043     /**
41044      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41045      *    This contains a reference to it.
41046      * @type {Roo.View}
41047      */
41048     view : false,
41049     
41050       /**
41051      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41052      * <pre><code>
41053
41054 layout.addxtype({
41055        xtype : 'Form',
41056        items: [ .... ]
41057    }
41058 );
41059
41060 </code></pre>
41061      * @param {Object} cfg Xtype definition of item to add.
41062      */
41063     
41064     
41065     getChildContainer: function () {
41066         return this.getEl();
41067     },
41068     
41069     
41070     onScroll : function(e)
41071     {
41072         this.fireEvent('scroll', this, e);
41073     }
41074     
41075     
41076     /*
41077         var  ret = new Roo.factory(cfg);
41078         return ret;
41079         
41080         
41081         // add form..
41082         if (cfg.xtype.match(/^Form$/)) {
41083             
41084             var el;
41085             //if (this.footer) {
41086             //    el = this.footer.container.insertSibling(false, 'before');
41087             //} else {
41088                 el = this.el.createChild();
41089             //}
41090
41091             this.form = new  Roo.form.Form(cfg);
41092             
41093             
41094             if ( this.form.allItems.length) {
41095                 this.form.render(el.dom);
41096             }
41097             return this.form;
41098         }
41099         // should only have one of theses..
41100         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41101             // views.. should not be just added - used named prop 'view''
41102             
41103             cfg.el = this.el.appendChild(document.createElement("div"));
41104             // factory?
41105             
41106             var ret = new Roo.factory(cfg);
41107              
41108              ret.render && ret.render(false, ''); // render blank..
41109             this.view = ret;
41110             return ret;
41111         }
41112         return false;
41113     }
41114     \*/
41115 });
41116  
41117 /**
41118  * @class Roo.bootstrap.panel.Grid
41119  * @extends Roo.bootstrap.panel.Content
41120  * @constructor
41121  * Create a new GridPanel.
41122  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41123  * @param {Object} config A the config object
41124   
41125  */
41126
41127
41128
41129 Roo.bootstrap.panel.Grid = function(config)
41130 {
41131     
41132       
41133     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41134         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41135
41136     config.el = this.wrapper;
41137     //this.el = this.wrapper;
41138     
41139       if (config.container) {
41140         // ctor'ed from a Border/panel.grid
41141         
41142         
41143         this.wrapper.setStyle("overflow", "hidden");
41144         this.wrapper.addClass('roo-grid-container');
41145
41146     }
41147     
41148     
41149     if(config.toolbar){
41150         var tool_el = this.wrapper.createChild();    
41151         this.toolbar = Roo.factory(config.toolbar);
41152         var ti = [];
41153         if (config.toolbar.items) {
41154             ti = config.toolbar.items ;
41155             delete config.toolbar.items ;
41156         }
41157         
41158         var nitems = [];
41159         this.toolbar.render(tool_el);
41160         for(var i =0;i < ti.length;i++) {
41161           //  Roo.log(['add child', items[i]]);
41162             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41163         }
41164         this.toolbar.items = nitems;
41165         
41166         delete config.toolbar;
41167     }
41168     
41169     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41170     config.grid.scrollBody = true;;
41171     config.grid.monitorWindowResize = false; // turn off autosizing
41172     config.grid.autoHeight = false;
41173     config.grid.autoWidth = false;
41174     
41175     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41176     
41177     if (config.background) {
41178         // render grid on panel activation (if panel background)
41179         this.on('activate', function(gp) {
41180             if (!gp.grid.rendered) {
41181                 gp.grid.render(this.wrapper);
41182                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41183             }
41184         });
41185             
41186     } else {
41187         this.grid.render(this.wrapper);
41188         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41189
41190     }
41191     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41192     // ??? needed ??? config.el = this.wrapper;
41193     
41194     
41195     
41196   
41197     // xtype created footer. - not sure if will work as we normally have to render first..
41198     if (this.footer && !this.footer.el && this.footer.xtype) {
41199         
41200         var ctr = this.grid.getView().getFooterPanel(true);
41201         this.footer.dataSource = this.grid.dataSource;
41202         this.footer = Roo.factory(this.footer, Roo);
41203         this.footer.render(ctr);
41204         
41205     }
41206     
41207     
41208     
41209     
41210      
41211 };
41212
41213 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41214     getId : function(){
41215         return this.grid.id;
41216     },
41217     
41218     /**
41219      * Returns the grid for this panel
41220      * @return {Roo.bootstrap.Table} 
41221      */
41222     getGrid : function(){
41223         return this.grid;    
41224     },
41225     
41226     setSize : function(width, height){
41227         if(!this.ignoreResize(width, height)){
41228             var grid = this.grid;
41229             var size = this.adjustForComponents(width, height);
41230             // tfoot is not a footer?
41231           
41232             
41233             var gridel = grid.getGridEl();
41234             gridel.setSize(size.width, size.height);
41235             
41236             var tbd = grid.getGridEl().select('tbody', true).first();
41237             var thd = grid.getGridEl().select('thead',true).first();
41238             var tbf= grid.getGridEl().select('tfoot', true).first();
41239
41240             if (tbf) {
41241                 size.height -= tbf.getHeight();
41242             }
41243             if (thd) {
41244                 size.height -= thd.getHeight();
41245             }
41246             
41247             tbd.setSize(size.width, size.height );
41248             // this is for the account management tab -seems to work there.
41249             var thd = grid.getGridEl().select('thead',true).first();
41250             //if (tbd) {
41251             //    tbd.setSize(size.width, size.height - thd.getHeight());
41252             //}
41253              
41254             grid.autoSize();
41255         }
41256     },
41257      
41258     
41259     
41260     beforeSlide : function(){
41261         this.grid.getView().scroller.clip();
41262     },
41263     
41264     afterSlide : function(){
41265         this.grid.getView().scroller.unclip();
41266     },
41267     
41268     destroy : function(){
41269         this.grid.destroy();
41270         delete this.grid;
41271         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41272     }
41273 });
41274
41275 /**
41276  * @class Roo.bootstrap.panel.Nest
41277  * @extends Roo.bootstrap.panel.Content
41278  * @constructor
41279  * Create a new Panel, that can contain a layout.Border.
41280  * 
41281  * 
41282  * @param {Roo.BorderLayout} layout The layout for this panel
41283  * @param {String/Object} config A string to set only the title or a config object
41284  */
41285 Roo.bootstrap.panel.Nest = function(config)
41286 {
41287     // construct with only one argument..
41288     /* FIXME - implement nicer consturctors
41289     if (layout.layout) {
41290         config = layout;
41291         layout = config.layout;
41292         delete config.layout;
41293     }
41294     if (layout.xtype && !layout.getEl) {
41295         // then layout needs constructing..
41296         layout = Roo.factory(layout, Roo);
41297     }
41298     */
41299     
41300     config.el =  config.layout.getEl();
41301     
41302     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41303     
41304     config.layout.monitorWindowResize = false; // turn off autosizing
41305     this.layout = config.layout;
41306     this.layout.getEl().addClass("roo-layout-nested-layout");
41307     this.layout.parent = this;
41308     
41309     
41310     
41311     
41312 };
41313
41314 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41315
41316     setSize : function(width, height){
41317         if(!this.ignoreResize(width, height)){
41318             var size = this.adjustForComponents(width, height);
41319             var el = this.layout.getEl();
41320             if (size.height < 1) {
41321                 el.setWidth(size.width);   
41322             } else {
41323                 el.setSize(size.width, size.height);
41324             }
41325             var touch = el.dom.offsetWidth;
41326             this.layout.layout();
41327             // ie requires a double layout on the first pass
41328             if(Roo.isIE && !this.initialized){
41329                 this.initialized = true;
41330                 this.layout.layout();
41331             }
41332         }
41333     },
41334     
41335     // activate all subpanels if not currently active..
41336     
41337     setActiveState : function(active){
41338         this.active = active;
41339         this.setActiveClass(active);
41340         
41341         if(!active){
41342             this.fireEvent("deactivate", this);
41343             return;
41344         }
41345         
41346         this.fireEvent("activate", this);
41347         // not sure if this should happen before or after..
41348         if (!this.layout) {
41349             return; // should not happen..
41350         }
41351         var reg = false;
41352         for (var r in this.layout.regions) {
41353             reg = this.layout.getRegion(r);
41354             if (reg.getActivePanel()) {
41355                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41356                 reg.setActivePanel(reg.getActivePanel());
41357                 continue;
41358             }
41359             if (!reg.panels.length) {
41360                 continue;
41361             }
41362             reg.showPanel(reg.getPanel(0));
41363         }
41364         
41365         
41366         
41367         
41368     },
41369     
41370     /**
41371      * Returns the nested BorderLayout for this panel
41372      * @return {Roo.BorderLayout} 
41373      */
41374     getLayout : function(){
41375         return this.layout;
41376     },
41377     
41378      /**
41379      * Adds a xtype elements to the layout of the nested panel
41380      * <pre><code>
41381
41382 panel.addxtype({
41383        xtype : 'ContentPanel',
41384        region: 'west',
41385        items: [ .... ]
41386    }
41387 );
41388
41389 panel.addxtype({
41390         xtype : 'NestedLayoutPanel',
41391         region: 'west',
41392         layout: {
41393            center: { },
41394            west: { }   
41395         },
41396         items : [ ... list of content panels or nested layout panels.. ]
41397    }
41398 );
41399 </code></pre>
41400      * @param {Object} cfg Xtype definition of item to add.
41401      */
41402     addxtype : function(cfg) {
41403         return this.layout.addxtype(cfg);
41404     
41405     }
41406 });/*
41407  * Based on:
41408  * Ext JS Library 1.1.1
41409  * Copyright(c) 2006-2007, Ext JS, LLC.
41410  *
41411  * Originally Released Under LGPL - original licence link has changed is not relivant.
41412  *
41413  * Fork - LGPL
41414  * <script type="text/javascript">
41415  */
41416 /**
41417  * @class Roo.TabPanel
41418  * @extends Roo.util.Observable
41419  * A lightweight tab container.
41420  * <br><br>
41421  * Usage:
41422  * <pre><code>
41423 // basic tabs 1, built from existing content
41424 var tabs = new Roo.TabPanel("tabs1");
41425 tabs.addTab("script", "View Script");
41426 tabs.addTab("markup", "View Markup");
41427 tabs.activate("script");
41428
41429 // more advanced tabs, built from javascript
41430 var jtabs = new Roo.TabPanel("jtabs");
41431 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41432
41433 // set up the UpdateManager
41434 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41435 var updater = tab2.getUpdateManager();
41436 updater.setDefaultUrl("ajax1.htm");
41437 tab2.on('activate', updater.refresh, updater, true);
41438
41439 // Use setUrl for Ajax loading
41440 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41441 tab3.setUrl("ajax2.htm", null, true);
41442
41443 // Disabled tab
41444 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41445 tab4.disable();
41446
41447 jtabs.activate("jtabs-1");
41448  * </code></pre>
41449  * @constructor
41450  * Create a new TabPanel.
41451  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41452  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41453  */
41454 Roo.bootstrap.panel.Tabs = function(config){
41455     /**
41456     * The container element for this TabPanel.
41457     * @type Roo.Element
41458     */
41459     this.el = Roo.get(config.el);
41460     delete config.el;
41461     if(config){
41462         if(typeof config == "boolean"){
41463             this.tabPosition = config ? "bottom" : "top";
41464         }else{
41465             Roo.apply(this, config);
41466         }
41467     }
41468     
41469     if(this.tabPosition == "bottom"){
41470         // if tabs are at the bottom = create the body first.
41471         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41472         this.el.addClass("roo-tabs-bottom");
41473     }
41474     // next create the tabs holders
41475     
41476     if (this.tabPosition == "west"){
41477         
41478         var reg = this.region; // fake it..
41479         while (reg) {
41480             if (!reg.mgr.parent) {
41481                 break;
41482             }
41483             reg = reg.mgr.parent.region;
41484         }
41485         Roo.log("got nest?");
41486         Roo.log(reg);
41487         if (reg.mgr.getRegion('west')) {
41488             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41489             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41490             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41491             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41492             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41493         
41494             
41495         }
41496         
41497         
41498     } else {
41499      
41500         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41501         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41502         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41503         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41504     }
41505     
41506     
41507     if(Roo.isIE){
41508         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41509     }
41510     
41511     // finally - if tabs are at the top, then create the body last..
41512     if(this.tabPosition != "bottom"){
41513         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41514          * @type Roo.Element
41515          */
41516         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41517         this.el.addClass("roo-tabs-top");
41518     }
41519     this.items = [];
41520
41521     this.bodyEl.setStyle("position", "relative");
41522
41523     this.active = null;
41524     this.activateDelegate = this.activate.createDelegate(this);
41525
41526     this.addEvents({
41527         /**
41528          * @event tabchange
41529          * Fires when the active tab changes
41530          * @param {Roo.TabPanel} this
41531          * @param {Roo.TabPanelItem} activePanel The new active tab
41532          */
41533         "tabchange": true,
41534         /**
41535          * @event beforetabchange
41536          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41537          * @param {Roo.TabPanel} this
41538          * @param {Object} e Set cancel to true on this object to cancel the tab change
41539          * @param {Roo.TabPanelItem} tab The tab being changed to
41540          */
41541         "beforetabchange" : true
41542     });
41543
41544     Roo.EventManager.onWindowResize(this.onResize, this);
41545     this.cpad = this.el.getPadding("lr");
41546     this.hiddenCount = 0;
41547
41548
41549     // toolbar on the tabbar support...
41550     if (this.toolbar) {
41551         alert("no toolbar support yet");
41552         this.toolbar  = false;
41553         /*
41554         var tcfg = this.toolbar;
41555         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41556         this.toolbar = new Roo.Toolbar(tcfg);
41557         if (Roo.isSafari) {
41558             var tbl = tcfg.container.child('table', true);
41559             tbl.setAttribute('width', '100%');
41560         }
41561         */
41562         
41563     }
41564    
41565
41566
41567     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41568 };
41569
41570 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41571     /*
41572      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41573      */
41574     tabPosition : "top",
41575     /*
41576      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41577      */
41578     currentTabWidth : 0,
41579     /*
41580      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41581      */
41582     minTabWidth : 40,
41583     /*
41584      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41585      */
41586     maxTabWidth : 250,
41587     /*
41588      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41589      */
41590     preferredTabWidth : 175,
41591     /*
41592      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41593      */
41594     resizeTabs : false,
41595     /*
41596      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41597      */
41598     monitorResize : true,
41599     /*
41600      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41601      */
41602     toolbar : false,  // set by caller..
41603     
41604     region : false, /// set by caller
41605     
41606     disableTooltips : true, // not used yet...
41607
41608     /**
41609      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41610      * @param {String} id The id of the div to use <b>or create</b>
41611      * @param {String} text The text for the tab
41612      * @param {String} content (optional) Content to put in the TabPanelItem body
41613      * @param {Boolean} closable (optional) True to create a close icon on the tab
41614      * @return {Roo.TabPanelItem} The created TabPanelItem
41615      */
41616     addTab : function(id, text, content, closable, tpl)
41617     {
41618         var item = new Roo.bootstrap.panel.TabItem({
41619             panel: this,
41620             id : id,
41621             text : text,
41622             closable : closable,
41623             tpl : tpl
41624         });
41625         this.addTabItem(item);
41626         if(content){
41627             item.setContent(content);
41628         }
41629         return item;
41630     },
41631
41632     /**
41633      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41634      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41635      * @return {Roo.TabPanelItem}
41636      */
41637     getTab : function(id){
41638         return this.items[id];
41639     },
41640
41641     /**
41642      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41643      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41644      */
41645     hideTab : function(id){
41646         var t = this.items[id];
41647         if(!t.isHidden()){
41648            t.setHidden(true);
41649            this.hiddenCount++;
41650            this.autoSizeTabs();
41651         }
41652     },
41653
41654     /**
41655      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41656      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41657      */
41658     unhideTab : function(id){
41659         var t = this.items[id];
41660         if(t.isHidden()){
41661            t.setHidden(false);
41662            this.hiddenCount--;
41663            this.autoSizeTabs();
41664         }
41665     },
41666
41667     /**
41668      * Adds an existing {@link Roo.TabPanelItem}.
41669      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41670      */
41671     addTabItem : function(item)
41672     {
41673         this.items[item.id] = item;
41674         this.items.push(item);
41675         this.autoSizeTabs();
41676       //  if(this.resizeTabs){
41677     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41678   //         this.autoSizeTabs();
41679 //        }else{
41680 //            item.autoSize();
41681        // }
41682     },
41683
41684     /**
41685      * Removes a {@link Roo.TabPanelItem}.
41686      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41687      */
41688     removeTab : function(id){
41689         var items = this.items;
41690         var tab = items[id];
41691         if(!tab) { return; }
41692         var index = items.indexOf(tab);
41693         if(this.active == tab && items.length > 1){
41694             var newTab = this.getNextAvailable(index);
41695             if(newTab) {
41696                 newTab.activate();
41697             }
41698         }
41699         this.stripEl.dom.removeChild(tab.pnode.dom);
41700         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41701             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41702         }
41703         items.splice(index, 1);
41704         delete this.items[tab.id];
41705         tab.fireEvent("close", tab);
41706         tab.purgeListeners();
41707         this.autoSizeTabs();
41708     },
41709
41710     getNextAvailable : function(start){
41711         var items = this.items;
41712         var index = start;
41713         // look for a next tab that will slide over to
41714         // replace the one being removed
41715         while(index < items.length){
41716             var item = items[++index];
41717             if(item && !item.isHidden()){
41718                 return item;
41719             }
41720         }
41721         // if one isn't found select the previous tab (on the left)
41722         index = start;
41723         while(index >= 0){
41724             var item = items[--index];
41725             if(item && !item.isHidden()){
41726                 return item;
41727             }
41728         }
41729         return null;
41730     },
41731
41732     /**
41733      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41734      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41735      */
41736     disableTab : function(id){
41737         var tab = this.items[id];
41738         if(tab && this.active != tab){
41739             tab.disable();
41740         }
41741     },
41742
41743     /**
41744      * Enables a {@link Roo.TabPanelItem} that is disabled.
41745      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41746      */
41747     enableTab : function(id){
41748         var tab = this.items[id];
41749         tab.enable();
41750     },
41751
41752     /**
41753      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41754      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41755      * @return {Roo.TabPanelItem} The TabPanelItem.
41756      */
41757     activate : function(id)
41758     {
41759         //Roo.log('activite:'  + id);
41760         
41761         var tab = this.items[id];
41762         if(!tab){
41763             return null;
41764         }
41765         if(tab == this.active || tab.disabled){
41766             return tab;
41767         }
41768         var e = {};
41769         this.fireEvent("beforetabchange", this, e, tab);
41770         if(e.cancel !== true && !tab.disabled){
41771             if(this.active){
41772                 this.active.hide();
41773             }
41774             this.active = this.items[id];
41775             this.active.show();
41776             this.fireEvent("tabchange", this, this.active);
41777         }
41778         return tab;
41779     },
41780
41781     /**
41782      * Gets the active {@link Roo.TabPanelItem}.
41783      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41784      */
41785     getActiveTab : function(){
41786         return this.active;
41787     },
41788
41789     /**
41790      * Updates the tab body element to fit the height of the container element
41791      * for overflow scrolling
41792      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41793      */
41794     syncHeight : function(targetHeight){
41795         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41796         var bm = this.bodyEl.getMargins();
41797         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41798         this.bodyEl.setHeight(newHeight);
41799         return newHeight;
41800     },
41801
41802     onResize : function(){
41803         if(this.monitorResize){
41804             this.autoSizeTabs();
41805         }
41806     },
41807
41808     /**
41809      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41810      */
41811     beginUpdate : function(){
41812         this.updating = true;
41813     },
41814
41815     /**
41816      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41817      */
41818     endUpdate : function(){
41819         this.updating = false;
41820         this.autoSizeTabs();
41821     },
41822
41823     /**
41824      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41825      */
41826     autoSizeTabs : function()
41827     {
41828         var count = this.items.length;
41829         var vcount = count - this.hiddenCount;
41830         
41831         if (vcount < 2) {
41832             this.stripEl.hide();
41833         } else {
41834             this.stripEl.show();
41835         }
41836         
41837         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41838             return;
41839         }
41840         
41841         
41842         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41843         var availWidth = Math.floor(w / vcount);
41844         var b = this.stripBody;
41845         if(b.getWidth() > w){
41846             var tabs = this.items;
41847             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41848             if(availWidth < this.minTabWidth){
41849                 /*if(!this.sleft){    // incomplete scrolling code
41850                     this.createScrollButtons();
41851                 }
41852                 this.showScroll();
41853                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41854             }
41855         }else{
41856             if(this.currentTabWidth < this.preferredTabWidth){
41857                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41858             }
41859         }
41860     },
41861
41862     /**
41863      * Returns the number of tabs in this TabPanel.
41864      * @return {Number}
41865      */
41866      getCount : function(){
41867          return this.items.length;
41868      },
41869
41870     /**
41871      * Resizes all the tabs to the passed width
41872      * @param {Number} The new width
41873      */
41874     setTabWidth : function(width){
41875         this.currentTabWidth = width;
41876         for(var i = 0, len = this.items.length; i < len; i++) {
41877                 if(!this.items[i].isHidden()) {
41878                 this.items[i].setWidth(width);
41879             }
41880         }
41881     },
41882
41883     /**
41884      * Destroys this TabPanel
41885      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41886      */
41887     destroy : function(removeEl){
41888         Roo.EventManager.removeResizeListener(this.onResize, this);
41889         for(var i = 0, len = this.items.length; i < len; i++){
41890             this.items[i].purgeListeners();
41891         }
41892         if(removeEl === true){
41893             this.el.update("");
41894             this.el.remove();
41895         }
41896     },
41897     
41898     createStrip : function(container)
41899     {
41900         var strip = document.createElement("nav");
41901         strip.className = Roo.bootstrap.version == 4 ?
41902             "navbar-light bg-light" : 
41903             "navbar navbar-default"; //"x-tabs-wrap";
41904         container.appendChild(strip);
41905         return strip;
41906     },
41907     
41908     createStripList : function(strip)
41909     {
41910         // div wrapper for retard IE
41911         // returns the "tr" element.
41912         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41913         //'<div class="x-tabs-strip-wrap">'+
41914           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41915           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41916         return strip.firstChild; //.firstChild.firstChild.firstChild;
41917     },
41918     createBody : function(container)
41919     {
41920         var body = document.createElement("div");
41921         Roo.id(body, "tab-body");
41922         //Roo.fly(body).addClass("x-tabs-body");
41923         Roo.fly(body).addClass("tab-content");
41924         container.appendChild(body);
41925         return body;
41926     },
41927     createItemBody :function(bodyEl, id){
41928         var body = Roo.getDom(id);
41929         if(!body){
41930             body = document.createElement("div");
41931             body.id = id;
41932         }
41933         //Roo.fly(body).addClass("x-tabs-item-body");
41934         Roo.fly(body).addClass("tab-pane");
41935          bodyEl.insertBefore(body, bodyEl.firstChild);
41936         return body;
41937     },
41938     /** @private */
41939     createStripElements :  function(stripEl, text, closable, tpl)
41940     {
41941         var td = document.createElement("li"); // was td..
41942         td.className = 'nav-item';
41943         
41944         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41945         
41946         
41947         stripEl.appendChild(td);
41948         /*if(closable){
41949             td.className = "x-tabs-closable";
41950             if(!this.closeTpl){
41951                 this.closeTpl = new Roo.Template(
41952                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41953                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41954                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41955                 );
41956             }
41957             var el = this.closeTpl.overwrite(td, {"text": text});
41958             var close = el.getElementsByTagName("div")[0];
41959             var inner = el.getElementsByTagName("em")[0];
41960             return {"el": el, "close": close, "inner": inner};
41961         } else {
41962         */
41963         // not sure what this is..
41964 //            if(!this.tabTpl){
41965                 //this.tabTpl = new Roo.Template(
41966                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41967                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41968                 //);
41969 //                this.tabTpl = new Roo.Template(
41970 //                   '<a href="#">' +
41971 //                   '<span unselectable="on"' +
41972 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41973 //                            ' >{text}</span></a>'
41974 //                );
41975 //                
41976 //            }
41977
41978
41979             var template = tpl || this.tabTpl || false;
41980             
41981             if(!template){
41982                 template =  new Roo.Template(
41983                         Roo.bootstrap.version == 4 ? 
41984                             (
41985                                 '<a class="nav-link" href="#" unselectable="on"' +
41986                                      (this.disableTooltips ? '' : ' title="{text}"') +
41987                                      ' >{text}</a>'
41988                             ) : (
41989                                 '<a class="nav-link" href="#">' +
41990                                 '<span unselectable="on"' +
41991                                          (this.disableTooltips ? '' : ' title="{text}"') +
41992                                     ' >{text}</span></a>'
41993                             )
41994                 );
41995             }
41996             
41997             switch (typeof(template)) {
41998                 case 'object' :
41999                     break;
42000                 case 'string' :
42001                     template = new Roo.Template(template);
42002                     break;
42003                 default :
42004                     break;
42005             }
42006             
42007             var el = template.overwrite(td, {"text": text});
42008             
42009             var inner = el.getElementsByTagName("span")[0];
42010             
42011             return {"el": el, "inner": inner};
42012             
42013     }
42014         
42015     
42016 });
42017
42018 /**
42019  * @class Roo.TabPanelItem
42020  * @extends Roo.util.Observable
42021  * Represents an individual item (tab plus body) in a TabPanel.
42022  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42023  * @param {String} id The id of this TabPanelItem
42024  * @param {String} text The text for the tab of this TabPanelItem
42025  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42026  */
42027 Roo.bootstrap.panel.TabItem = function(config){
42028     /**
42029      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42030      * @type Roo.TabPanel
42031      */
42032     this.tabPanel = config.panel;
42033     /**
42034      * The id for this TabPanelItem
42035      * @type String
42036      */
42037     this.id = config.id;
42038     /** @private */
42039     this.disabled = false;
42040     /** @private */
42041     this.text = config.text;
42042     /** @private */
42043     this.loaded = false;
42044     this.closable = config.closable;
42045
42046     /**
42047      * The body element for this TabPanelItem.
42048      * @type Roo.Element
42049      */
42050     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42051     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42052     this.bodyEl.setStyle("display", "block");
42053     this.bodyEl.setStyle("zoom", "1");
42054     //this.hideAction();
42055
42056     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42057     /** @private */
42058     this.el = Roo.get(els.el);
42059     this.inner = Roo.get(els.inner, true);
42060      this.textEl = Roo.bootstrap.version == 4 ?
42061         this.el : Roo.get(this.el.dom.firstChild, true);
42062
42063     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42064     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42065
42066     
42067 //    this.el.on("mousedown", this.onTabMouseDown, this);
42068     this.el.on("click", this.onTabClick, this);
42069     /** @private */
42070     if(config.closable){
42071         var c = Roo.get(els.close, true);
42072         c.dom.title = this.closeText;
42073         c.addClassOnOver("close-over");
42074         c.on("click", this.closeClick, this);
42075      }
42076
42077     this.addEvents({
42078          /**
42079          * @event activate
42080          * Fires when this tab becomes the active tab.
42081          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42082          * @param {Roo.TabPanelItem} this
42083          */
42084         "activate": true,
42085         /**
42086          * @event beforeclose
42087          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42088          * @param {Roo.TabPanelItem} this
42089          * @param {Object} e Set cancel to true on this object to cancel the close.
42090          */
42091         "beforeclose": true,
42092         /**
42093          * @event close
42094          * Fires when this tab is closed.
42095          * @param {Roo.TabPanelItem} this
42096          */
42097          "close": true,
42098         /**
42099          * @event deactivate
42100          * Fires when this tab is no longer the active tab.
42101          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42102          * @param {Roo.TabPanelItem} this
42103          */
42104          "deactivate" : true
42105     });
42106     this.hidden = false;
42107
42108     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42109 };
42110
42111 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42112            {
42113     purgeListeners : function(){
42114        Roo.util.Observable.prototype.purgeListeners.call(this);
42115        this.el.removeAllListeners();
42116     },
42117     /**
42118      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42119      */
42120     show : function(){
42121         this.status_node.addClass("active");
42122         this.showAction();
42123         if(Roo.isOpera){
42124             this.tabPanel.stripWrap.repaint();
42125         }
42126         this.fireEvent("activate", this.tabPanel, this);
42127     },
42128
42129     /**
42130      * Returns true if this tab is the active tab.
42131      * @return {Boolean}
42132      */
42133     isActive : function(){
42134         return this.tabPanel.getActiveTab() == this;
42135     },
42136
42137     /**
42138      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42139      */
42140     hide : function(){
42141         this.status_node.removeClass("active");
42142         this.hideAction();
42143         this.fireEvent("deactivate", this.tabPanel, this);
42144     },
42145
42146     hideAction : function(){
42147         this.bodyEl.hide();
42148         this.bodyEl.setStyle("position", "absolute");
42149         this.bodyEl.setLeft("-20000px");
42150         this.bodyEl.setTop("-20000px");
42151     },
42152
42153     showAction : function(){
42154         this.bodyEl.setStyle("position", "relative");
42155         this.bodyEl.setTop("");
42156         this.bodyEl.setLeft("");
42157         this.bodyEl.show();
42158     },
42159
42160     /**
42161      * Set the tooltip for the tab.
42162      * @param {String} tooltip The tab's tooltip
42163      */
42164     setTooltip : function(text){
42165         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42166             this.textEl.dom.qtip = text;
42167             this.textEl.dom.removeAttribute('title');
42168         }else{
42169             this.textEl.dom.title = text;
42170         }
42171     },
42172
42173     onTabClick : function(e){
42174         e.preventDefault();
42175         this.tabPanel.activate(this.id);
42176     },
42177
42178     onTabMouseDown : function(e){
42179         e.preventDefault();
42180         this.tabPanel.activate(this.id);
42181     },
42182 /*
42183     getWidth : function(){
42184         return this.inner.getWidth();
42185     },
42186
42187     setWidth : function(width){
42188         var iwidth = width - this.linode.getPadding("lr");
42189         this.inner.setWidth(iwidth);
42190         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42191         this.linode.setWidth(width);
42192     },
42193 */
42194     /**
42195      * Show or hide the tab
42196      * @param {Boolean} hidden True to hide or false to show.
42197      */
42198     setHidden : function(hidden){
42199         this.hidden = hidden;
42200         this.linode.setStyle("display", hidden ? "none" : "");
42201     },
42202
42203     /**
42204      * Returns true if this tab is "hidden"
42205      * @return {Boolean}
42206      */
42207     isHidden : function(){
42208         return this.hidden;
42209     },
42210
42211     /**
42212      * Returns the text for this tab
42213      * @return {String}
42214      */
42215     getText : function(){
42216         return this.text;
42217     },
42218     /*
42219     autoSize : function(){
42220         //this.el.beginMeasure();
42221         this.textEl.setWidth(1);
42222         /*
42223          *  #2804 [new] Tabs in Roojs
42224          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42225          */
42226         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42227         //this.el.endMeasure();
42228     //},
42229
42230     /**
42231      * Sets the text for the tab (Note: this also sets the tooltip text)
42232      * @param {String} text The tab's text and tooltip
42233      */
42234     setText : function(text){
42235         this.text = text;
42236         this.textEl.update(text);
42237         this.setTooltip(text);
42238         //if(!this.tabPanel.resizeTabs){
42239         //    this.autoSize();
42240         //}
42241     },
42242     /**
42243      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42244      */
42245     activate : function(){
42246         this.tabPanel.activate(this.id);
42247     },
42248
42249     /**
42250      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42251      */
42252     disable : function(){
42253         if(this.tabPanel.active != this){
42254             this.disabled = true;
42255             this.status_node.addClass("disabled");
42256         }
42257     },
42258
42259     /**
42260      * Enables this TabPanelItem if it was previously disabled.
42261      */
42262     enable : function(){
42263         this.disabled = false;
42264         this.status_node.removeClass("disabled");
42265     },
42266
42267     /**
42268      * Sets the content for this TabPanelItem.
42269      * @param {String} content The content
42270      * @param {Boolean} loadScripts true to look for and load scripts
42271      */
42272     setContent : function(content, loadScripts){
42273         this.bodyEl.update(content, loadScripts);
42274     },
42275
42276     /**
42277      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42278      * @return {Roo.UpdateManager} The UpdateManager
42279      */
42280     getUpdateManager : function(){
42281         return this.bodyEl.getUpdateManager();
42282     },
42283
42284     /**
42285      * Set a URL to be used to load the content for this TabPanelItem.
42286      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42287      * @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)
42288      * @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)
42289      * @return {Roo.UpdateManager} The UpdateManager
42290      */
42291     setUrl : function(url, params, loadOnce){
42292         if(this.refreshDelegate){
42293             this.un('activate', this.refreshDelegate);
42294         }
42295         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42296         this.on("activate", this.refreshDelegate);
42297         return this.bodyEl.getUpdateManager();
42298     },
42299
42300     /** @private */
42301     _handleRefresh : function(url, params, loadOnce){
42302         if(!loadOnce || !this.loaded){
42303             var updater = this.bodyEl.getUpdateManager();
42304             updater.update(url, params, this._setLoaded.createDelegate(this));
42305         }
42306     },
42307
42308     /**
42309      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42310      *   Will fail silently if the setUrl method has not been called.
42311      *   This does not activate the panel, just updates its content.
42312      */
42313     refresh : function(){
42314         if(this.refreshDelegate){
42315            this.loaded = false;
42316            this.refreshDelegate();
42317         }
42318     },
42319
42320     /** @private */
42321     _setLoaded : function(){
42322         this.loaded = true;
42323     },
42324
42325     /** @private */
42326     closeClick : function(e){
42327         var o = {};
42328         e.stopEvent();
42329         this.fireEvent("beforeclose", this, o);
42330         if(o.cancel !== true){
42331             this.tabPanel.removeTab(this.id);
42332         }
42333     },
42334     /**
42335      * The text displayed in the tooltip for the close icon.
42336      * @type String
42337      */
42338     closeText : "Close this tab"
42339 });
42340 /**
42341 *    This script refer to:
42342 *    Title: International Telephone Input
42343 *    Author: Jack O'Connor
42344 *    Code version:  v12.1.12
42345 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42346 **/
42347
42348 Roo.bootstrap.PhoneInputData = function() {
42349     var d = [
42350       [
42351         "Afghanistan (‫افغانستان‬‎)",
42352         "af",
42353         "93"
42354       ],
42355       [
42356         "Albania (Shqipëri)",
42357         "al",
42358         "355"
42359       ],
42360       [
42361         "Algeria (‫الجزائر‬‎)",
42362         "dz",
42363         "213"
42364       ],
42365       [
42366         "American Samoa",
42367         "as",
42368         "1684"
42369       ],
42370       [
42371         "Andorra",
42372         "ad",
42373         "376"
42374       ],
42375       [
42376         "Angola",
42377         "ao",
42378         "244"
42379       ],
42380       [
42381         "Anguilla",
42382         "ai",
42383         "1264"
42384       ],
42385       [
42386         "Antigua and Barbuda",
42387         "ag",
42388         "1268"
42389       ],
42390       [
42391         "Argentina",
42392         "ar",
42393         "54"
42394       ],
42395       [
42396         "Armenia (Հայաստան)",
42397         "am",
42398         "374"
42399       ],
42400       [
42401         "Aruba",
42402         "aw",
42403         "297"
42404       ],
42405       [
42406         "Australia",
42407         "au",
42408         "61",
42409         0
42410       ],
42411       [
42412         "Austria (Österreich)",
42413         "at",
42414         "43"
42415       ],
42416       [
42417         "Azerbaijan (Azərbaycan)",
42418         "az",
42419         "994"
42420       ],
42421       [
42422         "Bahamas",
42423         "bs",
42424         "1242"
42425       ],
42426       [
42427         "Bahrain (‫البحرين‬‎)",
42428         "bh",
42429         "973"
42430       ],
42431       [
42432         "Bangladesh (বাংলাদেশ)",
42433         "bd",
42434         "880"
42435       ],
42436       [
42437         "Barbados",
42438         "bb",
42439         "1246"
42440       ],
42441       [
42442         "Belarus (Беларусь)",
42443         "by",
42444         "375"
42445       ],
42446       [
42447         "Belgium (België)",
42448         "be",
42449         "32"
42450       ],
42451       [
42452         "Belize",
42453         "bz",
42454         "501"
42455       ],
42456       [
42457         "Benin (Bénin)",
42458         "bj",
42459         "229"
42460       ],
42461       [
42462         "Bermuda",
42463         "bm",
42464         "1441"
42465       ],
42466       [
42467         "Bhutan (འབྲུག)",
42468         "bt",
42469         "975"
42470       ],
42471       [
42472         "Bolivia",
42473         "bo",
42474         "591"
42475       ],
42476       [
42477         "Bosnia and Herzegovina (Босна и Херцеговина)",
42478         "ba",
42479         "387"
42480       ],
42481       [
42482         "Botswana",
42483         "bw",
42484         "267"
42485       ],
42486       [
42487         "Brazil (Brasil)",
42488         "br",
42489         "55"
42490       ],
42491       [
42492         "British Indian Ocean Territory",
42493         "io",
42494         "246"
42495       ],
42496       [
42497         "British Virgin Islands",
42498         "vg",
42499         "1284"
42500       ],
42501       [
42502         "Brunei",
42503         "bn",
42504         "673"
42505       ],
42506       [
42507         "Bulgaria (България)",
42508         "bg",
42509         "359"
42510       ],
42511       [
42512         "Burkina Faso",
42513         "bf",
42514         "226"
42515       ],
42516       [
42517         "Burundi (Uburundi)",
42518         "bi",
42519         "257"
42520       ],
42521       [
42522         "Cambodia (កម្ពុជា)",
42523         "kh",
42524         "855"
42525       ],
42526       [
42527         "Cameroon (Cameroun)",
42528         "cm",
42529         "237"
42530       ],
42531       [
42532         "Canada",
42533         "ca",
42534         "1",
42535         1,
42536         ["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"]
42537       ],
42538       [
42539         "Cape Verde (Kabu Verdi)",
42540         "cv",
42541         "238"
42542       ],
42543       [
42544         "Caribbean Netherlands",
42545         "bq",
42546         "599",
42547         1
42548       ],
42549       [
42550         "Cayman Islands",
42551         "ky",
42552         "1345"
42553       ],
42554       [
42555         "Central African Republic (République centrafricaine)",
42556         "cf",
42557         "236"
42558       ],
42559       [
42560         "Chad (Tchad)",
42561         "td",
42562         "235"
42563       ],
42564       [
42565         "Chile",
42566         "cl",
42567         "56"
42568       ],
42569       [
42570         "China (中国)",
42571         "cn",
42572         "86"
42573       ],
42574       [
42575         "Christmas Island",
42576         "cx",
42577         "61",
42578         2
42579       ],
42580       [
42581         "Cocos (Keeling) Islands",
42582         "cc",
42583         "61",
42584         1
42585       ],
42586       [
42587         "Colombia",
42588         "co",
42589         "57"
42590       ],
42591       [
42592         "Comoros (‫جزر القمر‬‎)",
42593         "km",
42594         "269"
42595       ],
42596       [
42597         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42598         "cd",
42599         "243"
42600       ],
42601       [
42602         "Congo (Republic) (Congo-Brazzaville)",
42603         "cg",
42604         "242"
42605       ],
42606       [
42607         "Cook Islands",
42608         "ck",
42609         "682"
42610       ],
42611       [
42612         "Costa Rica",
42613         "cr",
42614         "506"
42615       ],
42616       [
42617         "Côte d’Ivoire",
42618         "ci",
42619         "225"
42620       ],
42621       [
42622         "Croatia (Hrvatska)",
42623         "hr",
42624         "385"
42625       ],
42626       [
42627         "Cuba",
42628         "cu",
42629         "53"
42630       ],
42631       [
42632         "Curaçao",
42633         "cw",
42634         "599",
42635         0
42636       ],
42637       [
42638         "Cyprus (Κύπρος)",
42639         "cy",
42640         "357"
42641       ],
42642       [
42643         "Czech Republic (Česká republika)",
42644         "cz",
42645         "420"
42646       ],
42647       [
42648         "Denmark (Danmark)",
42649         "dk",
42650         "45"
42651       ],
42652       [
42653         "Djibouti",
42654         "dj",
42655         "253"
42656       ],
42657       [
42658         "Dominica",
42659         "dm",
42660         "1767"
42661       ],
42662       [
42663         "Dominican Republic (República Dominicana)",
42664         "do",
42665         "1",
42666         2,
42667         ["809", "829", "849"]
42668       ],
42669       [
42670         "Ecuador",
42671         "ec",
42672         "593"
42673       ],
42674       [
42675         "Egypt (‫مصر‬‎)",
42676         "eg",
42677         "20"
42678       ],
42679       [
42680         "El Salvador",
42681         "sv",
42682         "503"
42683       ],
42684       [
42685         "Equatorial Guinea (Guinea Ecuatorial)",
42686         "gq",
42687         "240"
42688       ],
42689       [
42690         "Eritrea",
42691         "er",
42692         "291"
42693       ],
42694       [
42695         "Estonia (Eesti)",
42696         "ee",
42697         "372"
42698       ],
42699       [
42700         "Ethiopia",
42701         "et",
42702         "251"
42703       ],
42704       [
42705         "Falkland Islands (Islas Malvinas)",
42706         "fk",
42707         "500"
42708       ],
42709       [
42710         "Faroe Islands (Føroyar)",
42711         "fo",
42712         "298"
42713       ],
42714       [
42715         "Fiji",
42716         "fj",
42717         "679"
42718       ],
42719       [
42720         "Finland (Suomi)",
42721         "fi",
42722         "358",
42723         0
42724       ],
42725       [
42726         "France",
42727         "fr",
42728         "33"
42729       ],
42730       [
42731         "French Guiana (Guyane française)",
42732         "gf",
42733         "594"
42734       ],
42735       [
42736         "French Polynesia (Polynésie française)",
42737         "pf",
42738         "689"
42739       ],
42740       [
42741         "Gabon",
42742         "ga",
42743         "241"
42744       ],
42745       [
42746         "Gambia",
42747         "gm",
42748         "220"
42749       ],
42750       [
42751         "Georgia (საქართველო)",
42752         "ge",
42753         "995"
42754       ],
42755       [
42756         "Germany (Deutschland)",
42757         "de",
42758         "49"
42759       ],
42760       [
42761         "Ghana (Gaana)",
42762         "gh",
42763         "233"
42764       ],
42765       [
42766         "Gibraltar",
42767         "gi",
42768         "350"
42769       ],
42770       [
42771         "Greece (Ελλάδα)",
42772         "gr",
42773         "30"
42774       ],
42775       [
42776         "Greenland (Kalaallit Nunaat)",
42777         "gl",
42778         "299"
42779       ],
42780       [
42781         "Grenada",
42782         "gd",
42783         "1473"
42784       ],
42785       [
42786         "Guadeloupe",
42787         "gp",
42788         "590",
42789         0
42790       ],
42791       [
42792         "Guam",
42793         "gu",
42794         "1671"
42795       ],
42796       [
42797         "Guatemala",
42798         "gt",
42799         "502"
42800       ],
42801       [
42802         "Guernsey",
42803         "gg",
42804         "44",
42805         1
42806       ],
42807       [
42808         "Guinea (Guinée)",
42809         "gn",
42810         "224"
42811       ],
42812       [
42813         "Guinea-Bissau (Guiné Bissau)",
42814         "gw",
42815         "245"
42816       ],
42817       [
42818         "Guyana",
42819         "gy",
42820         "592"
42821       ],
42822       [
42823         "Haiti",
42824         "ht",
42825         "509"
42826       ],
42827       [
42828         "Honduras",
42829         "hn",
42830         "504"
42831       ],
42832       [
42833         "Hong Kong (香港)",
42834         "hk",
42835         "852"
42836       ],
42837       [
42838         "Hungary (Magyarország)",
42839         "hu",
42840         "36"
42841       ],
42842       [
42843         "Iceland (Ísland)",
42844         "is",
42845         "354"
42846       ],
42847       [
42848         "India (भारत)",
42849         "in",
42850         "91"
42851       ],
42852       [
42853         "Indonesia",
42854         "id",
42855         "62"
42856       ],
42857       [
42858         "Iran (‫ایران‬‎)",
42859         "ir",
42860         "98"
42861       ],
42862       [
42863         "Iraq (‫العراق‬‎)",
42864         "iq",
42865         "964"
42866       ],
42867       [
42868         "Ireland",
42869         "ie",
42870         "353"
42871       ],
42872       [
42873         "Isle of Man",
42874         "im",
42875         "44",
42876         2
42877       ],
42878       [
42879         "Israel (‫ישראל‬‎)",
42880         "il",
42881         "972"
42882       ],
42883       [
42884         "Italy (Italia)",
42885         "it",
42886         "39",
42887         0
42888       ],
42889       [
42890         "Jamaica",
42891         "jm",
42892         "1876"
42893       ],
42894       [
42895         "Japan (日本)",
42896         "jp",
42897         "81"
42898       ],
42899       [
42900         "Jersey",
42901         "je",
42902         "44",
42903         3
42904       ],
42905       [
42906         "Jordan (‫الأردن‬‎)",
42907         "jo",
42908         "962"
42909       ],
42910       [
42911         "Kazakhstan (Казахстан)",
42912         "kz",
42913         "7",
42914         1
42915       ],
42916       [
42917         "Kenya",
42918         "ke",
42919         "254"
42920       ],
42921       [
42922         "Kiribati",
42923         "ki",
42924         "686"
42925       ],
42926       [
42927         "Kosovo",
42928         "xk",
42929         "383"
42930       ],
42931       [
42932         "Kuwait (‫الكويت‬‎)",
42933         "kw",
42934         "965"
42935       ],
42936       [
42937         "Kyrgyzstan (Кыргызстан)",
42938         "kg",
42939         "996"
42940       ],
42941       [
42942         "Laos (ລາວ)",
42943         "la",
42944         "856"
42945       ],
42946       [
42947         "Latvia (Latvija)",
42948         "lv",
42949         "371"
42950       ],
42951       [
42952         "Lebanon (‫لبنان‬‎)",
42953         "lb",
42954         "961"
42955       ],
42956       [
42957         "Lesotho",
42958         "ls",
42959         "266"
42960       ],
42961       [
42962         "Liberia",
42963         "lr",
42964         "231"
42965       ],
42966       [
42967         "Libya (‫ليبيا‬‎)",
42968         "ly",
42969         "218"
42970       ],
42971       [
42972         "Liechtenstein",
42973         "li",
42974         "423"
42975       ],
42976       [
42977         "Lithuania (Lietuva)",
42978         "lt",
42979         "370"
42980       ],
42981       [
42982         "Luxembourg",
42983         "lu",
42984         "352"
42985       ],
42986       [
42987         "Macau (澳門)",
42988         "mo",
42989         "853"
42990       ],
42991       [
42992         "Macedonia (FYROM) (Македонија)",
42993         "mk",
42994         "389"
42995       ],
42996       [
42997         "Madagascar (Madagasikara)",
42998         "mg",
42999         "261"
43000       ],
43001       [
43002         "Malawi",
43003         "mw",
43004         "265"
43005       ],
43006       [
43007         "Malaysia",
43008         "my",
43009         "60"
43010       ],
43011       [
43012         "Maldives",
43013         "mv",
43014         "960"
43015       ],
43016       [
43017         "Mali",
43018         "ml",
43019         "223"
43020       ],
43021       [
43022         "Malta",
43023         "mt",
43024         "356"
43025       ],
43026       [
43027         "Marshall Islands",
43028         "mh",
43029         "692"
43030       ],
43031       [
43032         "Martinique",
43033         "mq",
43034         "596"
43035       ],
43036       [
43037         "Mauritania (‫موريتانيا‬‎)",
43038         "mr",
43039         "222"
43040       ],
43041       [
43042         "Mauritius (Moris)",
43043         "mu",
43044         "230"
43045       ],
43046       [
43047         "Mayotte",
43048         "yt",
43049         "262",
43050         1
43051       ],
43052       [
43053         "Mexico (México)",
43054         "mx",
43055         "52"
43056       ],
43057       [
43058         "Micronesia",
43059         "fm",
43060         "691"
43061       ],
43062       [
43063         "Moldova (Republica Moldova)",
43064         "md",
43065         "373"
43066       ],
43067       [
43068         "Monaco",
43069         "mc",
43070         "377"
43071       ],
43072       [
43073         "Mongolia (Монгол)",
43074         "mn",
43075         "976"
43076       ],
43077       [
43078         "Montenegro (Crna Gora)",
43079         "me",
43080         "382"
43081       ],
43082       [
43083         "Montserrat",
43084         "ms",
43085         "1664"
43086       ],
43087       [
43088         "Morocco (‫المغرب‬‎)",
43089         "ma",
43090         "212",
43091         0
43092       ],
43093       [
43094         "Mozambique (Moçambique)",
43095         "mz",
43096         "258"
43097       ],
43098       [
43099         "Myanmar (Burma) (မြန်မာ)",
43100         "mm",
43101         "95"
43102       ],
43103       [
43104         "Namibia (Namibië)",
43105         "na",
43106         "264"
43107       ],
43108       [
43109         "Nauru",
43110         "nr",
43111         "674"
43112       ],
43113       [
43114         "Nepal (नेपाल)",
43115         "np",
43116         "977"
43117       ],
43118       [
43119         "Netherlands (Nederland)",
43120         "nl",
43121         "31"
43122       ],
43123       [
43124         "New Caledonia (Nouvelle-Calédonie)",
43125         "nc",
43126         "687"
43127       ],
43128       [
43129         "New Zealand",
43130         "nz",
43131         "64"
43132       ],
43133       [
43134         "Nicaragua",
43135         "ni",
43136         "505"
43137       ],
43138       [
43139         "Niger (Nijar)",
43140         "ne",
43141         "227"
43142       ],
43143       [
43144         "Nigeria",
43145         "ng",
43146         "234"
43147       ],
43148       [
43149         "Niue",
43150         "nu",
43151         "683"
43152       ],
43153       [
43154         "Norfolk Island",
43155         "nf",
43156         "672"
43157       ],
43158       [
43159         "North Korea (조선 민주주의 인민 공화국)",
43160         "kp",
43161         "850"
43162       ],
43163       [
43164         "Northern Mariana Islands",
43165         "mp",
43166         "1670"
43167       ],
43168       [
43169         "Norway (Norge)",
43170         "no",
43171         "47",
43172         0
43173       ],
43174       [
43175         "Oman (‫عُمان‬‎)",
43176         "om",
43177         "968"
43178       ],
43179       [
43180         "Pakistan (‫پاکستان‬‎)",
43181         "pk",
43182         "92"
43183       ],
43184       [
43185         "Palau",
43186         "pw",
43187         "680"
43188       ],
43189       [
43190         "Palestine (‫فلسطين‬‎)",
43191         "ps",
43192         "970"
43193       ],
43194       [
43195         "Panama (Panamá)",
43196         "pa",
43197         "507"
43198       ],
43199       [
43200         "Papua New Guinea",
43201         "pg",
43202         "675"
43203       ],
43204       [
43205         "Paraguay",
43206         "py",
43207         "595"
43208       ],
43209       [
43210         "Peru (Perú)",
43211         "pe",
43212         "51"
43213       ],
43214       [
43215         "Philippines",
43216         "ph",
43217         "63"
43218       ],
43219       [
43220         "Poland (Polska)",
43221         "pl",
43222         "48"
43223       ],
43224       [
43225         "Portugal",
43226         "pt",
43227         "351"
43228       ],
43229       [
43230         "Puerto Rico",
43231         "pr",
43232         "1",
43233         3,
43234         ["787", "939"]
43235       ],
43236       [
43237         "Qatar (‫قطر‬‎)",
43238         "qa",
43239         "974"
43240       ],
43241       [
43242         "Réunion (La Réunion)",
43243         "re",
43244         "262",
43245         0
43246       ],
43247       [
43248         "Romania (România)",
43249         "ro",
43250         "40"
43251       ],
43252       [
43253         "Russia (Россия)",
43254         "ru",
43255         "7",
43256         0
43257       ],
43258       [
43259         "Rwanda",
43260         "rw",
43261         "250"
43262       ],
43263       [
43264         "Saint Barthélemy",
43265         "bl",
43266         "590",
43267         1
43268       ],
43269       [
43270         "Saint Helena",
43271         "sh",
43272         "290"
43273       ],
43274       [
43275         "Saint Kitts and Nevis",
43276         "kn",
43277         "1869"
43278       ],
43279       [
43280         "Saint Lucia",
43281         "lc",
43282         "1758"
43283       ],
43284       [
43285         "Saint Martin (Saint-Martin (partie française))",
43286         "mf",
43287         "590",
43288         2
43289       ],
43290       [
43291         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43292         "pm",
43293         "508"
43294       ],
43295       [
43296         "Saint Vincent and the Grenadines",
43297         "vc",
43298         "1784"
43299       ],
43300       [
43301         "Samoa",
43302         "ws",
43303         "685"
43304       ],
43305       [
43306         "San Marino",
43307         "sm",
43308         "378"
43309       ],
43310       [
43311         "São Tomé and Príncipe (São Tomé e Príncipe)",
43312         "st",
43313         "239"
43314       ],
43315       [
43316         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43317         "sa",
43318         "966"
43319       ],
43320       [
43321         "Senegal (Sénégal)",
43322         "sn",
43323         "221"
43324       ],
43325       [
43326         "Serbia (Србија)",
43327         "rs",
43328         "381"
43329       ],
43330       [
43331         "Seychelles",
43332         "sc",
43333         "248"
43334       ],
43335       [
43336         "Sierra Leone",
43337         "sl",
43338         "232"
43339       ],
43340       [
43341         "Singapore",
43342         "sg",
43343         "65"
43344       ],
43345       [
43346         "Sint Maarten",
43347         "sx",
43348         "1721"
43349       ],
43350       [
43351         "Slovakia (Slovensko)",
43352         "sk",
43353         "421"
43354       ],
43355       [
43356         "Slovenia (Slovenija)",
43357         "si",
43358         "386"
43359       ],
43360       [
43361         "Solomon Islands",
43362         "sb",
43363         "677"
43364       ],
43365       [
43366         "Somalia (Soomaaliya)",
43367         "so",
43368         "252"
43369       ],
43370       [
43371         "South Africa",
43372         "za",
43373         "27"
43374       ],
43375       [
43376         "South Korea (대한민국)",
43377         "kr",
43378         "82"
43379       ],
43380       [
43381         "South Sudan (‫جنوب السودان‬‎)",
43382         "ss",
43383         "211"
43384       ],
43385       [
43386         "Spain (España)",
43387         "es",
43388         "34"
43389       ],
43390       [
43391         "Sri Lanka (ශ්‍රී ලංකාව)",
43392         "lk",
43393         "94"
43394       ],
43395       [
43396         "Sudan (‫السودان‬‎)",
43397         "sd",
43398         "249"
43399       ],
43400       [
43401         "Suriname",
43402         "sr",
43403         "597"
43404       ],
43405       [
43406         "Svalbard and Jan Mayen",
43407         "sj",
43408         "47",
43409         1
43410       ],
43411       [
43412         "Swaziland",
43413         "sz",
43414         "268"
43415       ],
43416       [
43417         "Sweden (Sverige)",
43418         "se",
43419         "46"
43420       ],
43421       [
43422         "Switzerland (Schweiz)",
43423         "ch",
43424         "41"
43425       ],
43426       [
43427         "Syria (‫سوريا‬‎)",
43428         "sy",
43429         "963"
43430       ],
43431       [
43432         "Taiwan (台灣)",
43433         "tw",
43434         "886"
43435       ],
43436       [
43437         "Tajikistan",
43438         "tj",
43439         "992"
43440       ],
43441       [
43442         "Tanzania",
43443         "tz",
43444         "255"
43445       ],
43446       [
43447         "Thailand (ไทย)",
43448         "th",
43449         "66"
43450       ],
43451       [
43452         "Timor-Leste",
43453         "tl",
43454         "670"
43455       ],
43456       [
43457         "Togo",
43458         "tg",
43459         "228"
43460       ],
43461       [
43462         "Tokelau",
43463         "tk",
43464         "690"
43465       ],
43466       [
43467         "Tonga",
43468         "to",
43469         "676"
43470       ],
43471       [
43472         "Trinidad and Tobago",
43473         "tt",
43474         "1868"
43475       ],
43476       [
43477         "Tunisia (‫تونس‬‎)",
43478         "tn",
43479         "216"
43480       ],
43481       [
43482         "Turkey (Türkiye)",
43483         "tr",
43484         "90"
43485       ],
43486       [
43487         "Turkmenistan",
43488         "tm",
43489         "993"
43490       ],
43491       [
43492         "Turks and Caicos Islands",
43493         "tc",
43494         "1649"
43495       ],
43496       [
43497         "Tuvalu",
43498         "tv",
43499         "688"
43500       ],
43501       [
43502         "U.S. Virgin Islands",
43503         "vi",
43504         "1340"
43505       ],
43506       [
43507         "Uganda",
43508         "ug",
43509         "256"
43510       ],
43511       [
43512         "Ukraine (Україна)",
43513         "ua",
43514         "380"
43515       ],
43516       [
43517         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43518         "ae",
43519         "971"
43520       ],
43521       [
43522         "United Kingdom",
43523         "gb",
43524         "44",
43525         0
43526       ],
43527       [
43528         "United States",
43529         "us",
43530         "1",
43531         0
43532       ],
43533       [
43534         "Uruguay",
43535         "uy",
43536         "598"
43537       ],
43538       [
43539         "Uzbekistan (Oʻzbekiston)",
43540         "uz",
43541         "998"
43542       ],
43543       [
43544         "Vanuatu",
43545         "vu",
43546         "678"
43547       ],
43548       [
43549         "Vatican City (Città del Vaticano)",
43550         "va",
43551         "39",
43552         1
43553       ],
43554       [
43555         "Venezuela",
43556         "ve",
43557         "58"
43558       ],
43559       [
43560         "Vietnam (Việt Nam)",
43561         "vn",
43562         "84"
43563       ],
43564       [
43565         "Wallis and Futuna (Wallis-et-Futuna)",
43566         "wf",
43567         "681"
43568       ],
43569       [
43570         "Western Sahara (‫الصحراء الغربية‬‎)",
43571         "eh",
43572         "212",
43573         1
43574       ],
43575       [
43576         "Yemen (‫اليمن‬‎)",
43577         "ye",
43578         "967"
43579       ],
43580       [
43581         "Zambia",
43582         "zm",
43583         "260"
43584       ],
43585       [
43586         "Zimbabwe",
43587         "zw",
43588         "263"
43589       ],
43590       [
43591         "Åland Islands",
43592         "ax",
43593         "358",
43594         1
43595       ]
43596   ];
43597   
43598   return d;
43599 }/**
43600 *    This script refer to:
43601 *    Title: International Telephone Input
43602 *    Author: Jack O'Connor
43603 *    Code version:  v12.1.12
43604 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43605 **/
43606
43607 /**
43608  * @class Roo.bootstrap.PhoneInput
43609  * @extends Roo.bootstrap.TriggerField
43610  * An input with International dial-code selection
43611  
43612  * @cfg {String} defaultDialCode default '+852'
43613  * @cfg {Array} preferedCountries default []
43614   
43615  * @constructor
43616  * Create a new PhoneInput.
43617  * @param {Object} config Configuration options
43618  */
43619
43620 Roo.bootstrap.PhoneInput = function(config) {
43621     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43622 };
43623
43624 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43625         
43626         listWidth: undefined,
43627         
43628         selectedClass: 'active',
43629         
43630         invalidClass : "has-warning",
43631         
43632         validClass: 'has-success',
43633         
43634         allowed: '0123456789',
43635         
43636         max_length: 15,
43637         
43638         /**
43639          * @cfg {String} defaultDialCode The default dial code when initializing the input
43640          */
43641         defaultDialCode: '+852',
43642         
43643         /**
43644          * @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
43645          */
43646         preferedCountries: false,
43647         
43648         getAutoCreate : function()
43649         {
43650             var data = Roo.bootstrap.PhoneInputData();
43651             var align = this.labelAlign || this.parentLabelAlign();
43652             var id = Roo.id();
43653             
43654             this.allCountries = [];
43655             this.dialCodeMapping = [];
43656             
43657             for (var i = 0; i < data.length; i++) {
43658               var c = data[i];
43659               this.allCountries[i] = {
43660                 name: c[0],
43661                 iso2: c[1],
43662                 dialCode: c[2],
43663                 priority: c[3] || 0,
43664                 areaCodes: c[4] || null
43665               };
43666               this.dialCodeMapping[c[2]] = {
43667                   name: c[0],
43668                   iso2: c[1],
43669                   priority: c[3] || 0,
43670                   areaCodes: c[4] || null
43671               };
43672             }
43673             
43674             var cfg = {
43675                 cls: 'form-group',
43676                 cn: []
43677             };
43678             
43679             var input =  {
43680                 tag: 'input',
43681                 id : id,
43682                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43683                 maxlength: this.max_length,
43684                 cls : 'form-control tel-input',
43685                 autocomplete: 'new-password'
43686             };
43687             
43688             var hiddenInput = {
43689                 tag: 'input',
43690                 type: 'hidden',
43691                 cls: 'hidden-tel-input'
43692             };
43693             
43694             if (this.name) {
43695                 hiddenInput.name = this.name;
43696             }
43697             
43698             if (this.disabled) {
43699                 input.disabled = true;
43700             }
43701             
43702             var flag_container = {
43703                 tag: 'div',
43704                 cls: 'flag-box',
43705                 cn: [
43706                     {
43707                         tag: 'div',
43708                         cls: 'flag'
43709                     },
43710                     {
43711                         tag: 'div',
43712                         cls: 'caret'
43713                     }
43714                 ]
43715             };
43716             
43717             var box = {
43718                 tag: 'div',
43719                 cls: this.hasFeedback ? 'has-feedback' : '',
43720                 cn: [
43721                     hiddenInput,
43722                     input,
43723                     {
43724                         tag: 'input',
43725                         cls: 'dial-code-holder',
43726                         disabled: true
43727                     }
43728                 ]
43729             };
43730             
43731             var container = {
43732                 cls: 'roo-select2-container input-group',
43733                 cn: [
43734                     flag_container,
43735                     box
43736                 ]
43737             };
43738             
43739             if (this.fieldLabel.length) {
43740                 var indicator = {
43741                     tag: 'i',
43742                     tooltip: 'This field is required'
43743                 };
43744                 
43745                 var label = {
43746                     tag: 'label',
43747                     'for':  id,
43748                     cls: 'control-label',
43749                     cn: []
43750                 };
43751                 
43752                 var label_text = {
43753                     tag: 'span',
43754                     html: this.fieldLabel
43755                 };
43756                 
43757                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43758                 label.cn = [
43759                     indicator,
43760                     label_text
43761                 ];
43762                 
43763                 if(this.indicatorpos == 'right') {
43764                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43765                     label.cn = [
43766                         label_text,
43767                         indicator
43768                     ];
43769                 }
43770                 
43771                 if(align == 'left') {
43772                     container = {
43773                         tag: 'div',
43774                         cn: [
43775                             container
43776                         ]
43777                     };
43778                     
43779                     if(this.labelWidth > 12){
43780                         label.style = "width: " + this.labelWidth + 'px';
43781                     }
43782                     if(this.labelWidth < 13 && this.labelmd == 0){
43783                         this.labelmd = this.labelWidth;
43784                     }
43785                     if(this.labellg > 0){
43786                         label.cls += ' col-lg-' + this.labellg;
43787                         input.cls += ' col-lg-' + (12 - this.labellg);
43788                     }
43789                     if(this.labelmd > 0){
43790                         label.cls += ' col-md-' + this.labelmd;
43791                         container.cls += ' col-md-' + (12 - this.labelmd);
43792                     }
43793                     if(this.labelsm > 0){
43794                         label.cls += ' col-sm-' + this.labelsm;
43795                         container.cls += ' col-sm-' + (12 - this.labelsm);
43796                     }
43797                     if(this.labelxs > 0){
43798                         label.cls += ' col-xs-' + this.labelxs;
43799                         container.cls += ' col-xs-' + (12 - this.labelxs);
43800                     }
43801                 }
43802             }
43803             
43804             cfg.cn = [
43805                 label,
43806                 container
43807             ];
43808             
43809             var settings = this;
43810             
43811             ['xs','sm','md','lg'].map(function(size){
43812                 if (settings[size]) {
43813                     cfg.cls += ' col-' + size + '-' + settings[size];
43814                 }
43815             });
43816             
43817             this.store = new Roo.data.Store({
43818                 proxy : new Roo.data.MemoryProxy({}),
43819                 reader : new Roo.data.JsonReader({
43820                     fields : [
43821                         {
43822                             'name' : 'name',
43823                             'type' : 'string'
43824                         },
43825                         {
43826                             'name' : 'iso2',
43827                             'type' : 'string'
43828                         },
43829                         {
43830                             'name' : 'dialCode',
43831                             'type' : 'string'
43832                         },
43833                         {
43834                             'name' : 'priority',
43835                             'type' : 'string'
43836                         },
43837                         {
43838                             'name' : 'areaCodes',
43839                             'type' : 'string'
43840                         }
43841                     ]
43842                 })
43843             });
43844             
43845             if(!this.preferedCountries) {
43846                 this.preferedCountries = [
43847                     'hk',
43848                     'gb',
43849                     'us'
43850                 ];
43851             }
43852             
43853             var p = this.preferedCountries.reverse();
43854             
43855             if(p) {
43856                 for (var i = 0; i < p.length; i++) {
43857                     for (var j = 0; j < this.allCountries.length; j++) {
43858                         if(this.allCountries[j].iso2 == p[i]) {
43859                             var t = this.allCountries[j];
43860                             this.allCountries.splice(j,1);
43861                             this.allCountries.unshift(t);
43862                         }
43863                     } 
43864                 }
43865             }
43866             
43867             this.store.proxy.data = {
43868                 success: true,
43869                 data: this.allCountries
43870             };
43871             
43872             return cfg;
43873         },
43874         
43875         initEvents : function()
43876         {
43877             this.createList();
43878             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43879             
43880             this.indicator = this.indicatorEl();
43881             this.flag = this.flagEl();
43882             this.dialCodeHolder = this.dialCodeHolderEl();
43883             
43884             this.trigger = this.el.select('div.flag-box',true).first();
43885             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43886             
43887             var _this = this;
43888             
43889             (function(){
43890                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43891                 _this.list.setWidth(lw);
43892             }).defer(100);
43893             
43894             this.list.on('mouseover', this.onViewOver, this);
43895             this.list.on('mousemove', this.onViewMove, this);
43896             this.inputEl().on("keyup", this.onKeyUp, this);
43897             this.inputEl().on("keypress", this.onKeyPress, this);
43898             
43899             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43900
43901             this.view = new Roo.View(this.list, this.tpl, {
43902                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43903             });
43904             
43905             this.view.on('click', this.onViewClick, this);
43906             this.setValue(this.defaultDialCode);
43907         },
43908         
43909         onTriggerClick : function(e)
43910         {
43911             Roo.log('trigger click');
43912             if(this.disabled){
43913                 return;
43914             }
43915             
43916             if(this.isExpanded()){
43917                 this.collapse();
43918                 this.hasFocus = false;
43919             }else {
43920                 this.store.load({});
43921                 this.hasFocus = true;
43922                 this.expand();
43923             }
43924         },
43925         
43926         isExpanded : function()
43927         {
43928             return this.list.isVisible();
43929         },
43930         
43931         collapse : function()
43932         {
43933             if(!this.isExpanded()){
43934                 return;
43935             }
43936             this.list.hide();
43937             Roo.get(document).un('mousedown', this.collapseIf, this);
43938             Roo.get(document).un('mousewheel', this.collapseIf, this);
43939             this.fireEvent('collapse', this);
43940             this.validate();
43941         },
43942         
43943         expand : function()
43944         {
43945             Roo.log('expand');
43946
43947             if(this.isExpanded() || !this.hasFocus){
43948                 return;
43949             }
43950             
43951             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43952             this.list.setWidth(lw);
43953             
43954             this.list.show();
43955             this.restrictHeight();
43956             
43957             Roo.get(document).on('mousedown', this.collapseIf, this);
43958             Roo.get(document).on('mousewheel', this.collapseIf, this);
43959             
43960             this.fireEvent('expand', this);
43961         },
43962         
43963         restrictHeight : function()
43964         {
43965             this.list.alignTo(this.inputEl(), this.listAlign);
43966             this.list.alignTo(this.inputEl(), this.listAlign);
43967         },
43968         
43969         onViewOver : function(e, t)
43970         {
43971             if(this.inKeyMode){
43972                 return;
43973             }
43974             var item = this.view.findItemFromChild(t);
43975             
43976             if(item){
43977                 var index = this.view.indexOf(item);
43978                 this.select(index, false);
43979             }
43980         },
43981
43982         // private
43983         onViewClick : function(view, doFocus, el, e)
43984         {
43985             var index = this.view.getSelectedIndexes()[0];
43986             
43987             var r = this.store.getAt(index);
43988             
43989             if(r){
43990                 this.onSelect(r, index);
43991             }
43992             if(doFocus !== false && !this.blockFocus){
43993                 this.inputEl().focus();
43994             }
43995         },
43996         
43997         onViewMove : function(e, t)
43998         {
43999             this.inKeyMode = false;
44000         },
44001         
44002         select : function(index, scrollIntoView)
44003         {
44004             this.selectedIndex = index;
44005             this.view.select(index);
44006             if(scrollIntoView !== false){
44007                 var el = this.view.getNode(index);
44008                 if(el){
44009                     this.list.scrollChildIntoView(el, false);
44010                 }
44011             }
44012         },
44013         
44014         createList : function()
44015         {
44016             this.list = Roo.get(document.body).createChild({
44017                 tag: 'ul',
44018                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44019                 style: 'display:none'
44020             });
44021             
44022             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44023         },
44024         
44025         collapseIf : function(e)
44026         {
44027             var in_combo  = e.within(this.el);
44028             var in_list =  e.within(this.list);
44029             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44030             
44031             if (in_combo || in_list || is_list) {
44032                 return;
44033             }
44034             this.collapse();
44035         },
44036         
44037         onSelect : function(record, index)
44038         {
44039             if(this.fireEvent('beforeselect', this, record, index) !== false){
44040                 
44041                 this.setFlagClass(record.data.iso2);
44042                 this.setDialCode(record.data.dialCode);
44043                 this.hasFocus = false;
44044                 this.collapse();
44045                 this.fireEvent('select', this, record, index);
44046             }
44047         },
44048         
44049         flagEl : function()
44050         {
44051             var flag = this.el.select('div.flag',true).first();
44052             if(!flag){
44053                 return false;
44054             }
44055             return flag;
44056         },
44057         
44058         dialCodeHolderEl : function()
44059         {
44060             var d = this.el.select('input.dial-code-holder',true).first();
44061             if(!d){
44062                 return false;
44063             }
44064             return d;
44065         },
44066         
44067         setDialCode : function(v)
44068         {
44069             this.dialCodeHolder.dom.value = '+'+v;
44070         },
44071         
44072         setFlagClass : function(n)
44073         {
44074             this.flag.dom.className = 'flag '+n;
44075         },
44076         
44077         getValue : function()
44078         {
44079             var v = this.inputEl().getValue();
44080             if(this.dialCodeHolder) {
44081                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44082             }
44083             return v;
44084         },
44085         
44086         setValue : function(v)
44087         {
44088             var d = this.getDialCode(v);
44089             
44090             //invalid dial code
44091             if(v.length == 0 || !d || d.length == 0) {
44092                 if(this.rendered){
44093                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44094                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44095                 }
44096                 return;
44097             }
44098             
44099             //valid dial code
44100             this.setFlagClass(this.dialCodeMapping[d].iso2);
44101             this.setDialCode(d);
44102             this.inputEl().dom.value = v.replace('+'+d,'');
44103             this.hiddenEl().dom.value = this.getValue();
44104             
44105             this.validate();
44106         },
44107         
44108         getDialCode : function(v)
44109         {
44110             v = v ||  '';
44111             
44112             if (v.length == 0) {
44113                 return this.dialCodeHolder.dom.value;
44114             }
44115             
44116             var dialCode = "";
44117             if (v.charAt(0) != "+") {
44118                 return false;
44119             }
44120             var numericChars = "";
44121             for (var i = 1; i < v.length; i++) {
44122               var c = v.charAt(i);
44123               if (!isNaN(c)) {
44124                 numericChars += c;
44125                 if (this.dialCodeMapping[numericChars]) {
44126                   dialCode = v.substr(1, i);
44127                 }
44128                 if (numericChars.length == 4) {
44129                   break;
44130                 }
44131               }
44132             }
44133             return dialCode;
44134         },
44135         
44136         reset : function()
44137         {
44138             this.setValue(this.defaultDialCode);
44139             this.validate();
44140         },
44141         
44142         hiddenEl : function()
44143         {
44144             return this.el.select('input.hidden-tel-input',true).first();
44145         },
44146         
44147         // after setting val
44148         onKeyUp : function(e){
44149             this.setValue(this.getValue());
44150         },
44151         
44152         onKeyPress : function(e){
44153             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44154                 e.stopEvent();
44155             }
44156         }
44157         
44158 });
44159 /**
44160  * @class Roo.bootstrap.MoneyField
44161  * @extends Roo.bootstrap.ComboBox
44162  * Bootstrap MoneyField class
44163  * 
44164  * @constructor
44165  * Create a new MoneyField.
44166  * @param {Object} config Configuration options
44167  */
44168
44169 Roo.bootstrap.MoneyField = function(config) {
44170     
44171     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44172     
44173 };
44174
44175 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44176     
44177     /**
44178      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44179      */
44180     allowDecimals : true,
44181     /**
44182      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44183      */
44184     decimalSeparator : ".",
44185     /**
44186      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44187      */
44188     decimalPrecision : 0,
44189     /**
44190      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44191      */
44192     allowNegative : true,
44193     /**
44194      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44195      */
44196     allowZero: true,
44197     /**
44198      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44199      */
44200     minValue : Number.NEGATIVE_INFINITY,
44201     /**
44202      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44203      */
44204     maxValue : Number.MAX_VALUE,
44205     /**
44206      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44207      */
44208     minText : "The minimum value for this field is {0}",
44209     /**
44210      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44211      */
44212     maxText : "The maximum value for this field is {0}",
44213     /**
44214      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44215      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44216      */
44217     nanText : "{0} is not a valid number",
44218     /**
44219      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44220      */
44221     castInt : true,
44222     /**
44223      * @cfg {String} defaults currency of the MoneyField
44224      * value should be in lkey
44225      */
44226     defaultCurrency : false,
44227     /**
44228      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44229      */
44230     thousandsDelimiter : false,
44231     /**
44232      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44233      */
44234     max_length: false,
44235     
44236     inputlg : 9,
44237     inputmd : 9,
44238     inputsm : 9,
44239     inputxs : 6,
44240     
44241     store : false,
44242     
44243     getAutoCreate : function()
44244     {
44245         var align = this.labelAlign || this.parentLabelAlign();
44246         
44247         var id = Roo.id();
44248
44249         var cfg = {
44250             cls: 'form-group',
44251             cn: []
44252         };
44253
44254         var input =  {
44255             tag: 'input',
44256             id : id,
44257             cls : 'form-control roo-money-amount-input',
44258             autocomplete: 'new-password'
44259         };
44260         
44261         var hiddenInput = {
44262             tag: 'input',
44263             type: 'hidden',
44264             id: Roo.id(),
44265             cls: 'hidden-number-input'
44266         };
44267         
44268         if(this.max_length) {
44269             input.maxlength = this.max_length; 
44270         }
44271         
44272         if (this.name) {
44273             hiddenInput.name = this.name;
44274         }
44275
44276         if (this.disabled) {
44277             input.disabled = true;
44278         }
44279
44280         var clg = 12 - this.inputlg;
44281         var cmd = 12 - this.inputmd;
44282         var csm = 12 - this.inputsm;
44283         var cxs = 12 - this.inputxs;
44284         
44285         var container = {
44286             tag : 'div',
44287             cls : 'row roo-money-field',
44288             cn : [
44289                 {
44290                     tag : 'div',
44291                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44292                     cn : [
44293                         {
44294                             tag : 'div',
44295                             cls: 'roo-select2-container input-group',
44296                             cn: [
44297                                 {
44298                                     tag : 'input',
44299                                     cls : 'form-control roo-money-currency-input',
44300                                     autocomplete: 'new-password',
44301                                     readOnly : 1,
44302                                     name : this.currencyName
44303                                 },
44304                                 {
44305                                     tag :'span',
44306                                     cls : 'input-group-addon',
44307                                     cn : [
44308                                         {
44309                                             tag: 'span',
44310                                             cls: 'caret'
44311                                         }
44312                                     ]
44313                                 }
44314                             ]
44315                         }
44316                     ]
44317                 },
44318                 {
44319                     tag : 'div',
44320                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44321                     cn : [
44322                         {
44323                             tag: 'div',
44324                             cls: this.hasFeedback ? 'has-feedback' : '',
44325                             cn: [
44326                                 input
44327                             ]
44328                         }
44329                     ]
44330                 }
44331             ]
44332             
44333         };
44334         
44335         if (this.fieldLabel.length) {
44336             var indicator = {
44337                 tag: 'i',
44338                 tooltip: 'This field is required'
44339             };
44340
44341             var label = {
44342                 tag: 'label',
44343                 'for':  id,
44344                 cls: 'control-label',
44345                 cn: []
44346             };
44347
44348             var label_text = {
44349                 tag: 'span',
44350                 html: this.fieldLabel
44351             };
44352
44353             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44354             label.cn = [
44355                 indicator,
44356                 label_text
44357             ];
44358
44359             if(this.indicatorpos == 'right') {
44360                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44361                 label.cn = [
44362                     label_text,
44363                     indicator
44364                 ];
44365             }
44366
44367             if(align == 'left') {
44368                 container = {
44369                     tag: 'div',
44370                     cn: [
44371                         container
44372                     ]
44373                 };
44374
44375                 if(this.labelWidth > 12){
44376                     label.style = "width: " + this.labelWidth + 'px';
44377                 }
44378                 if(this.labelWidth < 13 && this.labelmd == 0){
44379                     this.labelmd = this.labelWidth;
44380                 }
44381                 if(this.labellg > 0){
44382                     label.cls += ' col-lg-' + this.labellg;
44383                     input.cls += ' col-lg-' + (12 - this.labellg);
44384                 }
44385                 if(this.labelmd > 0){
44386                     label.cls += ' col-md-' + this.labelmd;
44387                     container.cls += ' col-md-' + (12 - this.labelmd);
44388                 }
44389                 if(this.labelsm > 0){
44390                     label.cls += ' col-sm-' + this.labelsm;
44391                     container.cls += ' col-sm-' + (12 - this.labelsm);
44392                 }
44393                 if(this.labelxs > 0){
44394                     label.cls += ' col-xs-' + this.labelxs;
44395                     container.cls += ' col-xs-' + (12 - this.labelxs);
44396                 }
44397             }
44398         }
44399
44400         cfg.cn = [
44401             label,
44402             container,
44403             hiddenInput
44404         ];
44405         
44406         var settings = this;
44407
44408         ['xs','sm','md','lg'].map(function(size){
44409             if (settings[size]) {
44410                 cfg.cls += ' col-' + size + '-' + settings[size];
44411             }
44412         });
44413         
44414         return cfg;
44415     },
44416     
44417     initEvents : function()
44418     {
44419         this.indicator = this.indicatorEl();
44420         
44421         this.initCurrencyEvent();
44422         
44423         this.initNumberEvent();
44424     },
44425     
44426     initCurrencyEvent : function()
44427     {
44428         if (!this.store) {
44429             throw "can not find store for combo";
44430         }
44431         
44432         this.store = Roo.factory(this.store, Roo.data);
44433         this.store.parent = this;
44434         
44435         this.createList();
44436         
44437         this.triggerEl = this.el.select('.input-group-addon', true).first();
44438         
44439         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44440         
44441         var _this = this;
44442         
44443         (function(){
44444             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44445             _this.list.setWidth(lw);
44446         }).defer(100);
44447         
44448         this.list.on('mouseover', this.onViewOver, this);
44449         this.list.on('mousemove', this.onViewMove, this);
44450         this.list.on('scroll', this.onViewScroll, this);
44451         
44452         if(!this.tpl){
44453             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44454         }
44455         
44456         this.view = new Roo.View(this.list, this.tpl, {
44457             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44458         });
44459         
44460         this.view.on('click', this.onViewClick, this);
44461         
44462         this.store.on('beforeload', this.onBeforeLoad, this);
44463         this.store.on('load', this.onLoad, this);
44464         this.store.on('loadexception', this.onLoadException, this);
44465         
44466         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44467             "up" : function(e){
44468                 this.inKeyMode = true;
44469                 this.selectPrev();
44470             },
44471
44472             "down" : function(e){
44473                 if(!this.isExpanded()){
44474                     this.onTriggerClick();
44475                 }else{
44476                     this.inKeyMode = true;
44477                     this.selectNext();
44478                 }
44479             },
44480
44481             "enter" : function(e){
44482                 this.collapse();
44483                 
44484                 if(this.fireEvent("specialkey", this, e)){
44485                     this.onViewClick(false);
44486                 }
44487                 
44488                 return true;
44489             },
44490
44491             "esc" : function(e){
44492                 this.collapse();
44493             },
44494
44495             "tab" : function(e){
44496                 this.collapse();
44497                 
44498                 if(this.fireEvent("specialkey", this, e)){
44499                     this.onViewClick(false);
44500                 }
44501                 
44502                 return true;
44503             },
44504
44505             scope : this,
44506
44507             doRelay : function(foo, bar, hname){
44508                 if(hname == 'down' || this.scope.isExpanded()){
44509                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44510                 }
44511                 return true;
44512             },
44513
44514             forceKeyDown: true
44515         });
44516         
44517         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44518         
44519     },
44520     
44521     initNumberEvent : function(e)
44522     {
44523         this.inputEl().on("keydown" , this.fireKey,  this);
44524         this.inputEl().on("focus", this.onFocus,  this);
44525         this.inputEl().on("blur", this.onBlur,  this);
44526         
44527         this.inputEl().relayEvent('keyup', this);
44528         
44529         if(this.indicator){
44530             this.indicator.addClass('invisible');
44531         }
44532  
44533         this.originalValue = this.getValue();
44534         
44535         if(this.validationEvent == 'keyup'){
44536             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44537             this.inputEl().on('keyup', this.filterValidation, this);
44538         }
44539         else if(this.validationEvent !== false){
44540             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44541         }
44542         
44543         if(this.selectOnFocus){
44544             this.on("focus", this.preFocus, this);
44545             
44546         }
44547         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44548             this.inputEl().on("keypress", this.filterKeys, this);
44549         } else {
44550             this.inputEl().relayEvent('keypress', this);
44551         }
44552         
44553         var allowed = "0123456789";
44554         
44555         if(this.allowDecimals){
44556             allowed += this.decimalSeparator;
44557         }
44558         
44559         if(this.allowNegative){
44560             allowed += "-";
44561         }
44562         
44563         if(this.thousandsDelimiter) {
44564             allowed += ",";
44565         }
44566         
44567         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44568         
44569         var keyPress = function(e){
44570             
44571             var k = e.getKey();
44572             
44573             var c = e.getCharCode();
44574             
44575             if(
44576                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44577                     allowed.indexOf(String.fromCharCode(c)) === -1
44578             ){
44579                 e.stopEvent();
44580                 return;
44581             }
44582             
44583             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44584                 return;
44585             }
44586             
44587             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44588                 e.stopEvent();
44589             }
44590         };
44591         
44592         this.inputEl().on("keypress", keyPress, this);
44593         
44594     },
44595     
44596     onTriggerClick : function(e)
44597     {   
44598         if(this.disabled){
44599             return;
44600         }
44601         
44602         this.page = 0;
44603         this.loadNext = false;
44604         
44605         if(this.isExpanded()){
44606             this.collapse();
44607             return;
44608         }
44609         
44610         this.hasFocus = true;
44611         
44612         if(this.triggerAction == 'all') {
44613             this.doQuery(this.allQuery, true);
44614             return;
44615         }
44616         
44617         this.doQuery(this.getRawValue());
44618     },
44619     
44620     getCurrency : function()
44621     {   
44622         var v = this.currencyEl().getValue();
44623         
44624         return v;
44625     },
44626     
44627     restrictHeight : function()
44628     {
44629         this.list.alignTo(this.currencyEl(), this.listAlign);
44630         this.list.alignTo(this.currencyEl(), this.listAlign);
44631     },
44632     
44633     onViewClick : function(view, doFocus, el, e)
44634     {
44635         var index = this.view.getSelectedIndexes()[0];
44636         
44637         var r = this.store.getAt(index);
44638         
44639         if(r){
44640             this.onSelect(r, index);
44641         }
44642     },
44643     
44644     onSelect : function(record, index){
44645         
44646         if(this.fireEvent('beforeselect', this, record, index) !== false){
44647         
44648             this.setFromCurrencyData(index > -1 ? record.data : false);
44649             
44650             this.collapse();
44651             
44652             this.fireEvent('select', this, record, index);
44653         }
44654     },
44655     
44656     setFromCurrencyData : function(o)
44657     {
44658         var currency = '';
44659         
44660         this.lastCurrency = o;
44661         
44662         if (this.currencyField) {
44663             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44664         } else {
44665             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44666         }
44667         
44668         this.lastSelectionText = currency;
44669         
44670         //setting default currency
44671         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44672             this.setCurrency(this.defaultCurrency);
44673             return;
44674         }
44675         
44676         this.setCurrency(currency);
44677     },
44678     
44679     setFromData : function(o)
44680     {
44681         var c = {};
44682         
44683         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44684         
44685         this.setFromCurrencyData(c);
44686         
44687         var value = '';
44688         
44689         if (this.name) {
44690             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44691         } else {
44692             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44693         }
44694         
44695         this.setValue(value);
44696         
44697     },
44698     
44699     setCurrency : function(v)
44700     {   
44701         this.currencyValue = v;
44702         
44703         if(this.rendered){
44704             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44705             this.validate();
44706         }
44707     },
44708     
44709     setValue : function(v)
44710     {
44711         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44712         
44713         this.value = v;
44714         
44715         if(this.rendered){
44716             
44717             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44718             
44719             this.inputEl().dom.value = (v == '') ? '' :
44720                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44721             
44722             if(!this.allowZero && v === '0') {
44723                 this.hiddenEl().dom.value = '';
44724                 this.inputEl().dom.value = '';
44725             }
44726             
44727             this.validate();
44728         }
44729     },
44730     
44731     getRawValue : function()
44732     {
44733         var v = this.inputEl().getValue();
44734         
44735         return v;
44736     },
44737     
44738     getValue : function()
44739     {
44740         return this.fixPrecision(this.parseValue(this.getRawValue()));
44741     },
44742     
44743     parseValue : function(value)
44744     {
44745         if(this.thousandsDelimiter) {
44746             value += "";
44747             r = new RegExp(",", "g");
44748             value = value.replace(r, "");
44749         }
44750         
44751         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44752         return isNaN(value) ? '' : value;
44753         
44754     },
44755     
44756     fixPrecision : function(value)
44757     {
44758         if(this.thousandsDelimiter) {
44759             value += "";
44760             r = new RegExp(",", "g");
44761             value = value.replace(r, "");
44762         }
44763         
44764         var nan = isNaN(value);
44765         
44766         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44767             return nan ? '' : value;
44768         }
44769         return parseFloat(value).toFixed(this.decimalPrecision);
44770     },
44771     
44772     decimalPrecisionFcn : function(v)
44773     {
44774         return Math.floor(v);
44775     },
44776     
44777     validateValue : function(value)
44778     {
44779         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44780             return false;
44781         }
44782         
44783         var num = this.parseValue(value);
44784         
44785         if(isNaN(num)){
44786             this.markInvalid(String.format(this.nanText, value));
44787             return false;
44788         }
44789         
44790         if(num < this.minValue){
44791             this.markInvalid(String.format(this.minText, this.minValue));
44792             return false;
44793         }
44794         
44795         if(num > this.maxValue){
44796             this.markInvalid(String.format(this.maxText, this.maxValue));
44797             return false;
44798         }
44799         
44800         return true;
44801     },
44802     
44803     validate : function()
44804     {
44805         if(this.disabled || this.allowBlank){
44806             this.markValid();
44807             return true;
44808         }
44809         
44810         var currency = this.getCurrency();
44811         
44812         if(this.validateValue(this.getRawValue()) && currency.length){
44813             this.markValid();
44814             return true;
44815         }
44816         
44817         this.markInvalid();
44818         return false;
44819     },
44820     
44821     getName: function()
44822     {
44823         return this.name;
44824     },
44825     
44826     beforeBlur : function()
44827     {
44828         if(!this.castInt){
44829             return;
44830         }
44831         
44832         var v = this.parseValue(this.getRawValue());
44833         
44834         if(v || v == 0){
44835             this.setValue(v);
44836         }
44837     },
44838     
44839     onBlur : function()
44840     {
44841         this.beforeBlur();
44842         
44843         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44844             //this.el.removeClass(this.focusClass);
44845         }
44846         
44847         this.hasFocus = false;
44848         
44849         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44850             this.validate();
44851         }
44852         
44853         var v = this.getValue();
44854         
44855         if(String(v) !== String(this.startValue)){
44856             this.fireEvent('change', this, v, this.startValue);
44857         }
44858         
44859         this.fireEvent("blur", this);
44860     },
44861     
44862     inputEl : function()
44863     {
44864         return this.el.select('.roo-money-amount-input', true).first();
44865     },
44866     
44867     currencyEl : function()
44868     {
44869         return this.el.select('.roo-money-currency-input', true).first();
44870     },
44871     
44872     hiddenEl : function()
44873     {
44874         return this.el.select('input.hidden-number-input',true).first();
44875     }
44876     
44877 });/**
44878  * @class Roo.bootstrap.BezierSignature
44879  * @extends Roo.bootstrap.Component
44880  * Bootstrap BezierSignature class
44881  * This script refer to:
44882  *    Title: Signature Pad
44883  *    Author: szimek
44884  *    Availability: https://github.com/szimek/signature_pad
44885  *
44886  * @constructor
44887  * Create a new BezierSignature
44888  * @param {Object} config The config object
44889  */
44890
44891 Roo.bootstrap.BezierSignature = function(config){
44892     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44893     this.addEvents({
44894         "resize" : true
44895     });
44896 };
44897
44898 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44899 {
44900      
44901     curve_data: [],
44902     
44903     is_empty: true,
44904     
44905     mouse_btn_down: true,
44906     
44907     /**
44908      * @cfg {int} canvas height
44909      */
44910     canvas_height: '200px',
44911     
44912     /**
44913      * @cfg {float|function} Radius of a single dot.
44914      */ 
44915     dot_size: false,
44916     
44917     /**
44918      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44919      */
44920     min_width: 0.5,
44921     
44922     /**
44923      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44924      */
44925     max_width: 2.5,
44926     
44927     /**
44928      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44929      */
44930     throttle: 16,
44931     
44932     /**
44933      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44934      */
44935     min_distance: 5,
44936     
44937     /**
44938      * @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.
44939      */
44940     bg_color: 'rgba(0, 0, 0, 0)',
44941     
44942     /**
44943      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44944      */
44945     dot_color: 'black',
44946     
44947     /**
44948      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44949      */ 
44950     velocity_filter_weight: 0.7,
44951     
44952     /**
44953      * @cfg {function} Callback when stroke begin. 
44954      */
44955     onBegin: false,
44956     
44957     /**
44958      * @cfg {function} Callback when stroke end.
44959      */
44960     onEnd: false,
44961     
44962     getAutoCreate : function()
44963     {
44964         var cls = 'roo-signature column';
44965         
44966         if(this.cls){
44967             cls += ' ' + this.cls;
44968         }
44969         
44970         var col_sizes = [
44971             'lg',
44972             'md',
44973             'sm',
44974             'xs'
44975         ];
44976         
44977         for(var i = 0; i < col_sizes.length; i++) {
44978             if(this[col_sizes[i]]) {
44979                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44980             }
44981         }
44982         
44983         var cfg = {
44984             tag: 'div',
44985             cls: cls,
44986             cn: [
44987                 {
44988                     tag: 'div',
44989                     cls: 'roo-signature-body',
44990                     cn: [
44991                         {
44992                             tag: 'canvas',
44993                             cls: 'roo-signature-body-canvas',
44994                             height: this.canvas_height,
44995                             width: this.canvas_width
44996                         }
44997                     ]
44998                 },
44999                 {
45000                     tag: 'input',
45001                     type: 'file',
45002                     style: 'display: none'
45003                 }
45004             ]
45005         };
45006         
45007         return cfg;
45008     },
45009     
45010     initEvents: function() 
45011     {
45012         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45013         
45014         var canvas = this.canvasEl();
45015         
45016         // mouse && touch event swapping...
45017         canvas.dom.style.touchAction = 'none';
45018         canvas.dom.style.msTouchAction = 'none';
45019         
45020         this.mouse_btn_down = false;
45021         canvas.on('mousedown', this._handleMouseDown, this);
45022         canvas.on('mousemove', this._handleMouseMove, this);
45023         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45024         
45025         if (window.PointerEvent) {
45026             canvas.on('pointerdown', this._handleMouseDown, this);
45027             canvas.on('pointermove', this._handleMouseMove, this);
45028             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45029         }
45030         
45031         if ('ontouchstart' in window) {
45032             canvas.on('touchstart', this._handleTouchStart, this);
45033             canvas.on('touchmove', this._handleTouchMove, this);
45034             canvas.on('touchend', this._handleTouchEnd, this);
45035         }
45036         
45037         Roo.EventManager.onWindowResize(this.resize, this, true);
45038         
45039         // file input event
45040         this.fileEl().on('change', this.uploadImage, this);
45041         
45042         this.clear();
45043         
45044         this.resize();
45045     },
45046     
45047     resize: function(){
45048         
45049         var canvas = this.canvasEl().dom;
45050         var ctx = this.canvasElCtx();
45051         var img_data = false;
45052         
45053         if(canvas.width > 0) {
45054             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45055         }
45056         // setting canvas width will clean img data
45057         canvas.width = 0;
45058         
45059         var style = window.getComputedStyle ? 
45060             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45061             
45062         var padding_left = parseInt(style.paddingLeft) || 0;
45063         var padding_right = parseInt(style.paddingRight) || 0;
45064         
45065         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45066         
45067         if(img_data) {
45068             ctx.putImageData(img_data, 0, 0);
45069         }
45070     },
45071     
45072     _handleMouseDown: function(e)
45073     {
45074         if (e.browserEvent.which === 1) {
45075             this.mouse_btn_down = true;
45076             this.strokeBegin(e);
45077         }
45078     },
45079     
45080     _handleMouseMove: function (e)
45081     {
45082         if (this.mouse_btn_down) {
45083             this.strokeMoveUpdate(e);
45084         }
45085     },
45086     
45087     _handleMouseUp: function (e)
45088     {
45089         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45090             this.mouse_btn_down = false;
45091             this.strokeEnd(e);
45092         }
45093     },
45094     
45095     _handleTouchStart: function (e) {
45096         
45097         e.preventDefault();
45098         if (e.browserEvent.targetTouches.length === 1) {
45099             // var touch = e.browserEvent.changedTouches[0];
45100             // this.strokeBegin(touch);
45101             
45102              this.strokeBegin(e); // assume e catching the correct xy...
45103         }
45104     },
45105     
45106     _handleTouchMove: function (e) {
45107         e.preventDefault();
45108         // var touch = event.targetTouches[0];
45109         // _this._strokeMoveUpdate(touch);
45110         this.strokeMoveUpdate(e);
45111     },
45112     
45113     _handleTouchEnd: function (e) {
45114         var wasCanvasTouched = e.target === this.canvasEl().dom;
45115         if (wasCanvasTouched) {
45116             e.preventDefault();
45117             // var touch = event.changedTouches[0];
45118             // _this._strokeEnd(touch);
45119             this.strokeEnd(e);
45120         }
45121     },
45122     
45123     reset: function () {
45124         this._lastPoints = [];
45125         this._lastVelocity = 0;
45126         this._lastWidth = (this.min_width + this.max_width) / 2;
45127         this.canvasElCtx().fillStyle = this.dot_color;
45128     },
45129     
45130     strokeMoveUpdate: function(e)
45131     {
45132         this.strokeUpdate(e);
45133         
45134         if (this.throttle) {
45135             this.throttleStroke(this.strokeUpdate, this.throttle);
45136         }
45137         else {
45138             this.strokeUpdate(e);
45139         }
45140     },
45141     
45142     strokeBegin: function(e)
45143     {
45144         var newPointGroup = {
45145             color: this.dot_color,
45146             points: []
45147         };
45148         
45149         if (typeof this.onBegin === 'function') {
45150             this.onBegin(e);
45151         }
45152         
45153         this.curve_data.push(newPointGroup);
45154         this.reset();
45155         this.strokeUpdate(e);
45156     },
45157     
45158     strokeUpdate: function(e)
45159     {
45160         var rect = this.canvasEl().dom.getBoundingClientRect();
45161         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45162         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45163         var lastPoints = lastPointGroup.points;
45164         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45165         var isLastPointTooClose = lastPoint
45166             ? point.distanceTo(lastPoint) <= this.min_distance
45167             : false;
45168         var color = lastPointGroup.color;
45169         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45170             var curve = this.addPoint(point);
45171             if (!lastPoint) {
45172                 this.drawDot({color: color, point: point});
45173             }
45174             else if (curve) {
45175                 this.drawCurve({color: color, curve: curve});
45176             }
45177             lastPoints.push({
45178                 time: point.time,
45179                 x: point.x,
45180                 y: point.y
45181             });
45182         }
45183     },
45184     
45185     strokeEnd: function(e)
45186     {
45187         this.strokeUpdate(e);
45188         if (typeof this.onEnd === 'function') {
45189             this.onEnd(e);
45190         }
45191     },
45192     
45193     addPoint:  function (point) {
45194         var _lastPoints = this._lastPoints;
45195         _lastPoints.push(point);
45196         if (_lastPoints.length > 2) {
45197             if (_lastPoints.length === 3) {
45198                 _lastPoints.unshift(_lastPoints[0]);
45199             }
45200             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45201             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45202             _lastPoints.shift();
45203             return curve;
45204         }
45205         return null;
45206     },
45207     
45208     calculateCurveWidths: function (startPoint, endPoint) {
45209         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45210             (1 - this.velocity_filter_weight) * this._lastVelocity;
45211
45212         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45213         var widths = {
45214             end: newWidth,
45215             start: this._lastWidth
45216         };
45217         
45218         this._lastVelocity = velocity;
45219         this._lastWidth = newWidth;
45220         return widths;
45221     },
45222     
45223     drawDot: function (_a) {
45224         var color = _a.color, point = _a.point;
45225         var ctx = this.canvasElCtx();
45226         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45227         ctx.beginPath();
45228         this.drawCurveSegment(point.x, point.y, width);
45229         ctx.closePath();
45230         ctx.fillStyle = color;
45231         ctx.fill();
45232     },
45233     
45234     drawCurve: function (_a) {
45235         var color = _a.color, curve = _a.curve;
45236         var ctx = this.canvasElCtx();
45237         var widthDelta = curve.endWidth - curve.startWidth;
45238         var drawSteps = Math.floor(curve.length()) * 2;
45239         ctx.beginPath();
45240         ctx.fillStyle = color;
45241         for (var i = 0; i < drawSteps; i += 1) {
45242         var t = i / drawSteps;
45243         var tt = t * t;
45244         var ttt = tt * t;
45245         var u = 1 - t;
45246         var uu = u * u;
45247         var uuu = uu * u;
45248         var x = uuu * curve.startPoint.x;
45249         x += 3 * uu * t * curve.control1.x;
45250         x += 3 * u * tt * curve.control2.x;
45251         x += ttt * curve.endPoint.x;
45252         var y = uuu * curve.startPoint.y;
45253         y += 3 * uu * t * curve.control1.y;
45254         y += 3 * u * tt * curve.control2.y;
45255         y += ttt * curve.endPoint.y;
45256         var width = curve.startWidth + ttt * widthDelta;
45257         this.drawCurveSegment(x, y, width);
45258         }
45259         ctx.closePath();
45260         ctx.fill();
45261     },
45262     
45263     drawCurveSegment: function (x, y, width) {
45264         var ctx = this.canvasElCtx();
45265         ctx.moveTo(x, y);
45266         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45267         this.is_empty = false;
45268     },
45269     
45270     clear: function()
45271     {
45272         var ctx = this.canvasElCtx();
45273         var canvas = this.canvasEl().dom;
45274         ctx.fillStyle = this.bg_color;
45275         ctx.clearRect(0, 0, canvas.width, canvas.height);
45276         ctx.fillRect(0, 0, canvas.width, canvas.height);
45277         this.curve_data = [];
45278         this.reset();
45279         this.is_empty = true;
45280     },
45281     
45282     fileEl: function()
45283     {
45284         return  this.el.select('input',true).first();
45285     },
45286     
45287     canvasEl: function()
45288     {
45289         return this.el.select('canvas',true).first();
45290     },
45291     
45292     canvasElCtx: function()
45293     {
45294         return this.el.select('canvas',true).first().dom.getContext('2d');
45295     },
45296     
45297     getImage: function(type)
45298     {
45299         if(this.is_empty) {
45300             return false;
45301         }
45302         
45303         // encryption ?
45304         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45305     },
45306     
45307     drawFromImage: function(img_src)
45308     {
45309         var img = new Image();
45310         
45311         img.onload = function(){
45312             this.canvasElCtx().drawImage(img, 0, 0);
45313         }.bind(this);
45314         
45315         img.src = img_src;
45316         
45317         this.is_empty = false;
45318     },
45319     
45320     selectImage: function()
45321     {
45322         this.fileEl().dom.click();
45323     },
45324     
45325     uploadImage: function(e)
45326     {
45327         var reader = new FileReader();
45328         
45329         reader.onload = function(e){
45330             var img = new Image();
45331             img.onload = function(){
45332                 this.reset();
45333                 this.canvasElCtx().drawImage(img, 0, 0);
45334             }.bind(this);
45335             img.src = e.target.result;
45336         }.bind(this);
45337         
45338         reader.readAsDataURL(e.target.files[0]);
45339     },
45340     
45341     // Bezier Point Constructor
45342     Point: (function () {
45343         function Point(x, y, time) {
45344             this.x = x;
45345             this.y = y;
45346             this.time = time || Date.now();
45347         }
45348         Point.prototype.distanceTo = function (start) {
45349             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45350         };
45351         Point.prototype.equals = function (other) {
45352             return this.x === other.x && this.y === other.y && this.time === other.time;
45353         };
45354         Point.prototype.velocityFrom = function (start) {
45355             return this.time !== start.time
45356             ? this.distanceTo(start) / (this.time - start.time)
45357             : 0;
45358         };
45359         return Point;
45360     }()),
45361     
45362     
45363     // Bezier Constructor
45364     Bezier: (function () {
45365         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45366             this.startPoint = startPoint;
45367             this.control2 = control2;
45368             this.control1 = control1;
45369             this.endPoint = endPoint;
45370             this.startWidth = startWidth;
45371             this.endWidth = endWidth;
45372         }
45373         Bezier.fromPoints = function (points, widths, scope) {
45374             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45375             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45376             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45377         };
45378         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45379             var dx1 = s1.x - s2.x;
45380             var dy1 = s1.y - s2.y;
45381             var dx2 = s2.x - s3.x;
45382             var dy2 = s2.y - s3.y;
45383             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45384             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45385             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45386             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45387             var dxm = m1.x - m2.x;
45388             var dym = m1.y - m2.y;
45389             var k = l2 / (l1 + l2);
45390             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45391             var tx = s2.x - cm.x;
45392             var ty = s2.y - cm.y;
45393             return {
45394                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45395                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45396             };
45397         };
45398         Bezier.prototype.length = function () {
45399             var steps = 10;
45400             var length = 0;
45401             var px;
45402             var py;
45403             for (var i = 0; i <= steps; i += 1) {
45404                 var t = i / steps;
45405                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45406                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45407                 if (i > 0) {
45408                     var xdiff = cx - px;
45409                     var ydiff = cy - py;
45410                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45411                 }
45412                 px = cx;
45413                 py = cy;
45414             }
45415             return length;
45416         };
45417         Bezier.prototype.point = function (t, start, c1, c2, end) {
45418             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45419             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45420             + (3.0 * c2 * (1.0 - t) * t * t)
45421             + (end * t * t * t);
45422         };
45423         return Bezier;
45424     }()),
45425     
45426     throttleStroke: function(fn, wait) {
45427       if (wait === void 0) { wait = 250; }
45428       var previous = 0;
45429       var timeout = null;
45430       var result;
45431       var storedContext;
45432       var storedArgs;
45433       var later = function () {
45434           previous = Date.now();
45435           timeout = null;
45436           result = fn.apply(storedContext, storedArgs);
45437           if (!timeout) {
45438               storedContext = null;
45439               storedArgs = [];
45440           }
45441       };
45442       return function wrapper() {
45443           var args = [];
45444           for (var _i = 0; _i < arguments.length; _i++) {
45445               args[_i] = arguments[_i];
45446           }
45447           var now = Date.now();
45448           var remaining = wait - (now - previous);
45449           storedContext = this;
45450           storedArgs = args;
45451           if (remaining <= 0 || remaining > wait) {
45452               if (timeout) {
45453                   clearTimeout(timeout);
45454                   timeout = null;
45455               }
45456               previous = now;
45457               result = fn.apply(storedContext, storedArgs);
45458               if (!timeout) {
45459                   storedContext = null;
45460                   storedArgs = [];
45461               }
45462           }
45463           else if (!timeout) {
45464               timeout = window.setTimeout(later, remaining);
45465           }
45466           return result;
45467       };
45468   }
45469   
45470 });
45471
45472  
45473
45474