Changed docs/json/roodata.jsondocs/src/Roo_HtmlEditorCore.js.htmldocs/src/Roo_form_Ht...
[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     /**
25613      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25614      */
25615     
25616     allowComments: false,
25617     // id of frame..
25618     frameId: false,
25619     
25620     // private properties
25621     validationEvent : false,
25622     deferHeight: true,
25623     initialized : false,
25624     activated : false,
25625     sourceEditMode : false,
25626     onFocus : Roo.emptyFn,
25627     iframePad:3,
25628     hideMode:'offsets',
25629     
25630     clearUp: true,
25631     
25632     // blacklist + whitelisted elements..
25633     black: false,
25634     white: false,
25635      
25636     bodyCls : '',
25637
25638     /**
25639      * Protected method that will not generally be called directly. It
25640      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25641      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25642      */
25643     getDocMarkup : function(){
25644         // body styles..
25645         var st = '';
25646         
25647         // inherit styels from page...?? 
25648         if (this.stylesheets === false) {
25649             
25650             Roo.get(document.head).select('style').each(function(node) {
25651                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25652             });
25653             
25654             Roo.get(document.head).select('link').each(function(node) { 
25655                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25656             });
25657             
25658         } else if (!this.stylesheets.length) {
25659                 // simple..
25660                 st = '<style type="text/css">' +
25661                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25662                    '</style>';
25663         } else {
25664             for (var i in this.stylesheets) { 
25665                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25666             }
25667             
25668         }
25669         
25670         st +=  '<style type="text/css">' +
25671             'IMG { cursor: pointer } ' +
25672         '</style>';
25673
25674         var cls = 'roo-htmleditor-body';
25675         
25676         if(this.bodyCls.length){
25677             cls += ' ' + this.bodyCls;
25678         }
25679         
25680         return '<html><head>' + st  +
25681             //<style type="text/css">' +
25682             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25683             //'</style>' +
25684             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25685     },
25686
25687     // private
25688     onRender : function(ct, position)
25689     {
25690         var _t = this;
25691         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25692         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25693         
25694         
25695         this.el.dom.style.border = '0 none';
25696         this.el.dom.setAttribute('tabIndex', -1);
25697         this.el.addClass('x-hidden hide');
25698         
25699         
25700         
25701         if(Roo.isIE){ // fix IE 1px bogus margin
25702             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25703         }
25704        
25705         
25706         this.frameId = Roo.id();
25707         
25708          
25709         
25710         var iframe = this.owner.wrap.createChild({
25711             tag: 'iframe',
25712             cls: 'form-control', // bootstrap..
25713             id: this.frameId,
25714             name: this.frameId,
25715             frameBorder : 'no',
25716             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25717         }, this.el
25718         );
25719         
25720         
25721         this.iframe = iframe.dom;
25722
25723          this.assignDocWin();
25724         
25725         this.doc.designMode = 'on';
25726        
25727         this.doc.open();
25728         this.doc.write(this.getDocMarkup());
25729         this.doc.close();
25730
25731         
25732         var task = { // must defer to wait for browser to be ready
25733             run : function(){
25734                 //console.log("run task?" + this.doc.readyState);
25735                 this.assignDocWin();
25736                 if(this.doc.body || this.doc.readyState == 'complete'){
25737                     try {
25738                         this.doc.designMode="on";
25739                     } catch (e) {
25740                         return;
25741                     }
25742                     Roo.TaskMgr.stop(task);
25743                     this.initEditor.defer(10, this);
25744                 }
25745             },
25746             interval : 10,
25747             duration: 10000,
25748             scope: this
25749         };
25750         Roo.TaskMgr.start(task);
25751
25752     },
25753
25754     // private
25755     onResize : function(w, h)
25756     {
25757          Roo.log('resize: ' +w + ',' + h );
25758         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25759         if(!this.iframe){
25760             return;
25761         }
25762         if(typeof w == 'number'){
25763             
25764             this.iframe.style.width = w + 'px';
25765         }
25766         if(typeof h == 'number'){
25767             
25768             this.iframe.style.height = h + 'px';
25769             if(this.doc){
25770                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25771             }
25772         }
25773         
25774     },
25775
25776     /**
25777      * Toggles the editor between standard and source edit mode.
25778      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25779      */
25780     toggleSourceEdit : function(sourceEditMode){
25781         
25782         this.sourceEditMode = sourceEditMode === true;
25783         
25784         if(this.sourceEditMode){
25785  
25786             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25787             
25788         }else{
25789             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25790             //this.iframe.className = '';
25791             this.deferFocus();
25792         }
25793         //this.setSize(this.owner.wrap.getSize());
25794         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25795     },
25796
25797     
25798   
25799
25800     /**
25801      * Protected method that will not generally be called directly. If you need/want
25802      * custom HTML cleanup, this is the method you should override.
25803      * @param {String} html The HTML to be cleaned
25804      * return {String} The cleaned HTML
25805      */
25806     cleanHtml : function(html){
25807         html = String(html);
25808         if(html.length > 5){
25809             if(Roo.isSafari){ // strip safari nonsense
25810                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25811             }
25812         }
25813         if(html == '&nbsp;'){
25814             html = '';
25815         }
25816         return html;
25817     },
25818
25819     /**
25820      * HTML Editor -> Textarea
25821      * Protected method that will not generally be called directly. Syncs the contents
25822      * of the editor iframe with the textarea.
25823      */
25824     syncValue : function(){
25825         if(this.initialized){
25826             var bd = (this.doc.body || this.doc.documentElement);
25827             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25828             var html = bd.innerHTML;
25829             if(Roo.isSafari){
25830                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25831                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25832                 if(m && m[1]){
25833                     html = '<div style="'+m[0]+'">' + html + '</div>';
25834                 }
25835             }
25836             html = this.cleanHtml(html);
25837             // fix up the special chars.. normaly like back quotes in word...
25838             // however we do not want to do this with chinese..
25839             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25840                 
25841                 var cc = match.charCodeAt();
25842
25843                 // Get the character value, handling surrogate pairs
25844                 if (match.length == 2) {
25845                     // It's a surrogate pair, calculate the Unicode code point
25846                     var high = match.charCodeAt(0) - 0xD800;
25847                     var low  = match.charCodeAt(1) - 0xDC00;
25848                     cc = (high * 0x400) + low + 0x10000;
25849                 }  else if (
25850                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25851                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25852                     (cc >= 0xf900 && cc < 0xfb00 )
25853                 ) {
25854                         return match;
25855                 }  
25856          
25857                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25858                 return "&#" + cc + ";";
25859                 
25860                 
25861             });
25862             
25863             
25864              
25865             if(this.owner.fireEvent('beforesync', this, html) !== false){
25866                 this.el.dom.value = html;
25867                 this.owner.fireEvent('sync', this, html);
25868             }
25869         }
25870     },
25871
25872     /**
25873      * Protected method that will not generally be called directly. Pushes the value of the textarea
25874      * into the iframe editor.
25875      */
25876     pushValue : function(){
25877         if(this.initialized){
25878             var v = this.el.dom.value.trim();
25879             
25880 //            if(v.length < 1){
25881 //                v = '&#160;';
25882 //            }
25883             
25884             if(this.owner.fireEvent('beforepush', this, v) !== false){
25885                 var d = (this.doc.body || this.doc.documentElement);
25886                 d.innerHTML = v;
25887                 this.cleanUpPaste();
25888                 this.el.dom.value = d.innerHTML;
25889                 this.owner.fireEvent('push', this, v);
25890             }
25891         }
25892     },
25893
25894     // private
25895     deferFocus : function(){
25896         this.focus.defer(10, this);
25897     },
25898
25899     // doc'ed in Field
25900     focus : function(){
25901         if(this.win && !this.sourceEditMode){
25902             this.win.focus();
25903         }else{
25904             this.el.focus();
25905         }
25906     },
25907     
25908     assignDocWin: function()
25909     {
25910         var iframe = this.iframe;
25911         
25912          if(Roo.isIE){
25913             this.doc = iframe.contentWindow.document;
25914             this.win = iframe.contentWindow;
25915         } else {
25916 //            if (!Roo.get(this.frameId)) {
25917 //                return;
25918 //            }
25919 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25920 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25921             
25922             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25923                 return;
25924             }
25925             
25926             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25927             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25928         }
25929     },
25930     
25931     // private
25932     initEditor : function(){
25933         //console.log("INIT EDITOR");
25934         this.assignDocWin();
25935         
25936         
25937         
25938         this.doc.designMode="on";
25939         this.doc.open();
25940         this.doc.write(this.getDocMarkup());
25941         this.doc.close();
25942         
25943         var dbody = (this.doc.body || this.doc.documentElement);
25944         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25945         // this copies styles from the containing element into thsi one..
25946         // not sure why we need all of this..
25947         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25948         
25949         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25950         //ss['background-attachment'] = 'fixed'; // w3c
25951         dbody.bgProperties = 'fixed'; // ie
25952         //Roo.DomHelper.applyStyles(dbody, ss);
25953         Roo.EventManager.on(this.doc, {
25954             //'mousedown': this.onEditorEvent,
25955             'mouseup': this.onEditorEvent,
25956             'dblclick': this.onEditorEvent,
25957             'click': this.onEditorEvent,
25958             'keyup': this.onEditorEvent,
25959             buffer:100,
25960             scope: this
25961         });
25962         if(Roo.isGecko){
25963             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25964         }
25965         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25966             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25967         }
25968         this.initialized = true;
25969
25970         this.owner.fireEvent('initialize', this);
25971         this.pushValue();
25972     },
25973
25974     // private
25975     onDestroy : function(){
25976         
25977         
25978         
25979         if(this.rendered){
25980             
25981             //for (var i =0; i < this.toolbars.length;i++) {
25982             //    // fixme - ask toolbars for heights?
25983             //    this.toolbars[i].onDestroy();
25984            // }
25985             
25986             //this.wrap.dom.innerHTML = '';
25987             //this.wrap.remove();
25988         }
25989     },
25990
25991     // private
25992     onFirstFocus : function(){
25993         
25994         this.assignDocWin();
25995         
25996         
25997         this.activated = true;
25998          
25999     
26000         if(Roo.isGecko){ // prevent silly gecko errors
26001             this.win.focus();
26002             var s = this.win.getSelection();
26003             if(!s.focusNode || s.focusNode.nodeType != 3){
26004                 var r = s.getRangeAt(0);
26005                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26006                 r.collapse(true);
26007                 this.deferFocus();
26008             }
26009             try{
26010                 this.execCmd('useCSS', true);
26011                 this.execCmd('styleWithCSS', false);
26012             }catch(e){}
26013         }
26014         this.owner.fireEvent('activate', this);
26015     },
26016
26017     // private
26018     adjustFont: function(btn){
26019         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26020         //if(Roo.isSafari){ // safari
26021         //    adjust *= 2;
26022        // }
26023         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26024         if(Roo.isSafari){ // safari
26025             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26026             v =  (v < 10) ? 10 : v;
26027             v =  (v > 48) ? 48 : v;
26028             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26029             
26030         }
26031         
26032         
26033         v = Math.max(1, v+adjust);
26034         
26035         this.execCmd('FontSize', v  );
26036     },
26037
26038     onEditorEvent : function(e)
26039     {
26040         this.owner.fireEvent('editorevent', this, e);
26041       //  this.updateToolbar();
26042         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26043     },
26044
26045     insertTag : function(tg)
26046     {
26047         // could be a bit smarter... -> wrap the current selected tRoo..
26048         if (tg.toLowerCase() == 'span' ||
26049             tg.toLowerCase() == 'code' ||
26050             tg.toLowerCase() == 'sup' ||
26051             tg.toLowerCase() == 'sub' 
26052             ) {
26053             
26054             range = this.createRange(this.getSelection());
26055             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26056             wrappingNode.appendChild(range.extractContents());
26057             range.insertNode(wrappingNode);
26058
26059             return;
26060             
26061             
26062             
26063         }
26064         this.execCmd("formatblock",   tg);
26065         
26066     },
26067     
26068     insertText : function(txt)
26069     {
26070         
26071         
26072         var range = this.createRange();
26073         range.deleteContents();
26074                //alert(Sender.getAttribute('label'));
26075                
26076         range.insertNode(this.doc.createTextNode(txt));
26077     } ,
26078     
26079      
26080
26081     /**
26082      * Executes a Midas editor command on the editor document and performs necessary focus and
26083      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26084      * @param {String} cmd The Midas command
26085      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26086      */
26087     relayCmd : function(cmd, value){
26088         this.win.focus();
26089         this.execCmd(cmd, value);
26090         this.owner.fireEvent('editorevent', this);
26091         //this.updateToolbar();
26092         this.owner.deferFocus();
26093     },
26094
26095     /**
26096      * Executes a Midas editor command directly on the editor document.
26097      * For visual commands, you should use {@link #relayCmd} instead.
26098      * <b>This should only be called after the editor is initialized.</b>
26099      * @param {String} cmd The Midas command
26100      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26101      */
26102     execCmd : function(cmd, value){
26103         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26104         this.syncValue();
26105     },
26106  
26107  
26108    
26109     /**
26110      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26111      * to insert tRoo.
26112      * @param {String} text | dom node.. 
26113      */
26114     insertAtCursor : function(text)
26115     {
26116         
26117         if(!this.activated){
26118             return;
26119         }
26120         /*
26121         if(Roo.isIE){
26122             this.win.focus();
26123             var r = this.doc.selection.createRange();
26124             if(r){
26125                 r.collapse(true);
26126                 r.pasteHTML(text);
26127                 this.syncValue();
26128                 this.deferFocus();
26129             
26130             }
26131             return;
26132         }
26133         */
26134         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26135             this.win.focus();
26136             
26137             
26138             // from jquery ui (MIT licenced)
26139             var range, node;
26140             var win = this.win;
26141             
26142             if (win.getSelection && win.getSelection().getRangeAt) {
26143                 range = win.getSelection().getRangeAt(0);
26144                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26145                 range.insertNode(node);
26146             } else if (win.document.selection && win.document.selection.createRange) {
26147                 // no firefox support
26148                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26149                 win.document.selection.createRange().pasteHTML(txt);
26150             } else {
26151                 // no firefox support
26152                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26153                 this.execCmd('InsertHTML', txt);
26154             } 
26155             
26156             this.syncValue();
26157             
26158             this.deferFocus();
26159         }
26160     },
26161  // private
26162     mozKeyPress : function(e){
26163         if(e.ctrlKey){
26164             var c = e.getCharCode(), cmd;
26165           
26166             if(c > 0){
26167                 c = String.fromCharCode(c).toLowerCase();
26168                 switch(c){
26169                     case 'b':
26170                         cmd = 'bold';
26171                         break;
26172                     case 'i':
26173                         cmd = 'italic';
26174                         break;
26175                     
26176                     case 'u':
26177                         cmd = 'underline';
26178                         break;
26179                     
26180                     case 'v':
26181                         this.cleanUpPaste.defer(100, this);
26182                         return;
26183                         
26184                 }
26185                 if(cmd){
26186                     this.win.focus();
26187                     this.execCmd(cmd);
26188                     this.deferFocus();
26189                     e.preventDefault();
26190                 }
26191                 
26192             }
26193         }
26194     },
26195
26196     // private
26197     fixKeys : function(){ // load time branching for fastest keydown performance
26198         if(Roo.isIE){
26199             return function(e){
26200                 var k = e.getKey(), r;
26201                 if(k == e.TAB){
26202                     e.stopEvent();
26203                     r = this.doc.selection.createRange();
26204                     if(r){
26205                         r.collapse(true);
26206                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26207                         this.deferFocus();
26208                     }
26209                     return;
26210                 }
26211                 
26212                 if(k == e.ENTER){
26213                     r = this.doc.selection.createRange();
26214                     if(r){
26215                         var target = r.parentElement();
26216                         if(!target || target.tagName.toLowerCase() != 'li'){
26217                             e.stopEvent();
26218                             r.pasteHTML('<br />');
26219                             r.collapse(false);
26220                             r.select();
26221                         }
26222                     }
26223                 }
26224                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26225                     this.cleanUpPaste.defer(100, this);
26226                     return;
26227                 }
26228                 
26229                 
26230             };
26231         }else if(Roo.isOpera){
26232             return function(e){
26233                 var k = e.getKey();
26234                 if(k == e.TAB){
26235                     e.stopEvent();
26236                     this.win.focus();
26237                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26238                     this.deferFocus();
26239                 }
26240                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26241                     this.cleanUpPaste.defer(100, this);
26242                     return;
26243                 }
26244                 
26245             };
26246         }else if(Roo.isSafari){
26247             return function(e){
26248                 var k = e.getKey();
26249                 
26250                 if(k == e.TAB){
26251                     e.stopEvent();
26252                     this.execCmd('InsertText','\t');
26253                     this.deferFocus();
26254                     return;
26255                 }
26256                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26257                     this.cleanUpPaste.defer(100, this);
26258                     return;
26259                 }
26260                 
26261              };
26262         }
26263     }(),
26264     
26265     getAllAncestors: function()
26266     {
26267         var p = this.getSelectedNode();
26268         var a = [];
26269         if (!p) {
26270             a.push(p); // push blank onto stack..
26271             p = this.getParentElement();
26272         }
26273         
26274         
26275         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26276             a.push(p);
26277             p = p.parentNode;
26278         }
26279         a.push(this.doc.body);
26280         return a;
26281     },
26282     lastSel : false,
26283     lastSelNode : false,
26284     
26285     
26286     getSelection : function() 
26287     {
26288         this.assignDocWin();
26289         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26290     },
26291     
26292     getSelectedNode: function() 
26293     {
26294         // this may only work on Gecko!!!
26295         
26296         // should we cache this!!!!
26297         
26298         
26299         
26300          
26301         var range = this.createRange(this.getSelection()).cloneRange();
26302         
26303         if (Roo.isIE) {
26304             var parent = range.parentElement();
26305             while (true) {
26306                 var testRange = range.duplicate();
26307                 testRange.moveToElementText(parent);
26308                 if (testRange.inRange(range)) {
26309                     break;
26310                 }
26311                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26312                     break;
26313                 }
26314                 parent = parent.parentElement;
26315             }
26316             return parent;
26317         }
26318         
26319         // is ancestor a text element.
26320         var ac =  range.commonAncestorContainer;
26321         if (ac.nodeType == 3) {
26322             ac = ac.parentNode;
26323         }
26324         
26325         var ar = ac.childNodes;
26326          
26327         var nodes = [];
26328         var other_nodes = [];
26329         var has_other_nodes = false;
26330         for (var i=0;i<ar.length;i++) {
26331             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26332                 continue;
26333             }
26334             // fullly contained node.
26335             
26336             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26337                 nodes.push(ar[i]);
26338                 continue;
26339             }
26340             
26341             // probably selected..
26342             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26343                 other_nodes.push(ar[i]);
26344                 continue;
26345             }
26346             // outer..
26347             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26348                 continue;
26349             }
26350             
26351             
26352             has_other_nodes = true;
26353         }
26354         if (!nodes.length && other_nodes.length) {
26355             nodes= other_nodes;
26356         }
26357         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26358             return false;
26359         }
26360         
26361         return nodes[0];
26362     },
26363     createRange: function(sel)
26364     {
26365         // this has strange effects when using with 
26366         // top toolbar - not sure if it's a great idea.
26367         //this.editor.contentWindow.focus();
26368         if (typeof sel != "undefined") {
26369             try {
26370                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26371             } catch(e) {
26372                 return this.doc.createRange();
26373             }
26374         } else {
26375             return this.doc.createRange();
26376         }
26377     },
26378     getParentElement: function()
26379     {
26380         
26381         this.assignDocWin();
26382         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26383         
26384         var range = this.createRange(sel);
26385          
26386         try {
26387             var p = range.commonAncestorContainer;
26388             while (p.nodeType == 3) { // text node
26389                 p = p.parentNode;
26390             }
26391             return p;
26392         } catch (e) {
26393             return null;
26394         }
26395     
26396     },
26397     /***
26398      *
26399      * Range intersection.. the hard stuff...
26400      *  '-1' = before
26401      *  '0' = hits..
26402      *  '1' = after.
26403      *         [ -- selected range --- ]
26404      *   [fail]                        [fail]
26405      *
26406      *    basically..
26407      *      if end is before start or  hits it. fail.
26408      *      if start is after end or hits it fail.
26409      *
26410      *   if either hits (but other is outside. - then it's not 
26411      *   
26412      *    
26413      **/
26414     
26415     
26416     // @see http://www.thismuchiknow.co.uk/?p=64.
26417     rangeIntersectsNode : function(range, node)
26418     {
26419         var nodeRange = node.ownerDocument.createRange();
26420         try {
26421             nodeRange.selectNode(node);
26422         } catch (e) {
26423             nodeRange.selectNodeContents(node);
26424         }
26425     
26426         var rangeStartRange = range.cloneRange();
26427         rangeStartRange.collapse(true);
26428     
26429         var rangeEndRange = range.cloneRange();
26430         rangeEndRange.collapse(false);
26431     
26432         var nodeStartRange = nodeRange.cloneRange();
26433         nodeStartRange.collapse(true);
26434     
26435         var nodeEndRange = nodeRange.cloneRange();
26436         nodeEndRange.collapse(false);
26437     
26438         return rangeStartRange.compareBoundaryPoints(
26439                  Range.START_TO_START, nodeEndRange) == -1 &&
26440                rangeEndRange.compareBoundaryPoints(
26441                  Range.START_TO_START, nodeStartRange) == 1;
26442         
26443          
26444     },
26445     rangeCompareNode : function(range, node)
26446     {
26447         var nodeRange = node.ownerDocument.createRange();
26448         try {
26449             nodeRange.selectNode(node);
26450         } catch (e) {
26451             nodeRange.selectNodeContents(node);
26452         }
26453         
26454         
26455         range.collapse(true);
26456     
26457         nodeRange.collapse(true);
26458      
26459         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26460         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26461          
26462         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26463         
26464         var nodeIsBefore   =  ss == 1;
26465         var nodeIsAfter    = ee == -1;
26466         
26467         if (nodeIsBefore && nodeIsAfter) {
26468             return 0; // outer
26469         }
26470         if (!nodeIsBefore && nodeIsAfter) {
26471             return 1; //right trailed.
26472         }
26473         
26474         if (nodeIsBefore && !nodeIsAfter) {
26475             return 2;  // left trailed.
26476         }
26477         // fully contined.
26478         return 3;
26479     },
26480
26481     // private? - in a new class?
26482     cleanUpPaste :  function()
26483     {
26484         // cleans up the whole document..
26485         Roo.log('cleanuppaste');
26486         
26487         this.cleanUpChildren(this.doc.body);
26488         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26489         if (clean != this.doc.body.innerHTML) {
26490             this.doc.body.innerHTML = clean;
26491         }
26492         
26493     },
26494     
26495     cleanWordChars : function(input) {// change the chars to hex code
26496         var he = Roo.HtmlEditorCore;
26497         
26498         var output = input;
26499         Roo.each(he.swapCodes, function(sw) { 
26500             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26501             
26502             output = output.replace(swapper, sw[1]);
26503         });
26504         
26505         return output;
26506     },
26507     
26508     
26509     cleanUpChildren : function (n)
26510     {
26511         if (!n.childNodes.length) {
26512             return;
26513         }
26514         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26515            this.cleanUpChild(n.childNodes[i]);
26516         }
26517     },
26518     
26519     
26520         
26521     
26522     cleanUpChild : function (node)
26523     {
26524         var ed = this;
26525         //console.log(node);
26526         if (node.nodeName == "#text") {
26527             // clean up silly Windows -- stuff?
26528             return; 
26529         }
26530         if (node.nodeName == "#comment" && !this.allowComments) {
26531             node.parentNode.removeChild(node);
26532             // clean up silly Windows -- stuff?
26533             return; 
26534         }
26535         var lcname = node.tagName.toLowerCase();
26536         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26537         // whitelist of tags..
26538         
26539         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26540             // remove node.
26541             node.parentNode.removeChild(node);
26542             return;
26543             
26544         }
26545         
26546         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26547         
26548         // spans with no attributes - just remove them..
26549         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26550             remove_keep_children = true;
26551         }
26552         
26553         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26554         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26555         
26556         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26557         //    remove_keep_children = true;
26558         //}
26559         
26560         if (remove_keep_children) {
26561             this.cleanUpChildren(node);
26562             // inserts everything just before this node...
26563             while (node.childNodes.length) {
26564                 var cn = node.childNodes[0];
26565                 node.removeChild(cn);
26566                 node.parentNode.insertBefore(cn, node);
26567             }
26568             node.parentNode.removeChild(node);
26569             return;
26570         }
26571         
26572         if (!node.attributes || !node.attributes.length) {
26573             
26574           
26575             
26576             
26577             this.cleanUpChildren(node);
26578             return;
26579         }
26580         
26581         function cleanAttr(n,v)
26582         {
26583             
26584             if (v.match(/^\./) || v.match(/^\//)) {
26585                 return;
26586             }
26587             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26588                 return;
26589             }
26590             if (v.match(/^#/)) {
26591                 return;
26592             }
26593             if (v.match(/^\{/)) { // allow template editing.
26594                 return;
26595             }
26596 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26597             node.removeAttribute(n);
26598             
26599         }
26600         
26601         var cwhite = this.cwhite;
26602         var cblack = this.cblack;
26603             
26604         function cleanStyle(n,v)
26605         {
26606             if (v.match(/expression/)) { //XSS?? should we even bother..
26607                 node.removeAttribute(n);
26608                 return;
26609             }
26610             
26611             var parts = v.split(/;/);
26612             var clean = [];
26613             
26614             Roo.each(parts, function(p) {
26615                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26616                 if (!p.length) {
26617                     return true;
26618                 }
26619                 var l = p.split(':').shift().replace(/\s+/g,'');
26620                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26621                 
26622                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26623 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26624                     //node.removeAttribute(n);
26625                     return true;
26626                 }
26627                 //Roo.log()
26628                 // only allow 'c whitelisted system attributes'
26629                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26630 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26631                     //node.removeAttribute(n);
26632                     return true;
26633                 }
26634                 
26635                 
26636                  
26637                 
26638                 clean.push(p);
26639                 return true;
26640             });
26641             if (clean.length) { 
26642                 node.setAttribute(n, clean.join(';'));
26643             } else {
26644                 node.removeAttribute(n);
26645             }
26646             
26647         }
26648         
26649         
26650         for (var i = node.attributes.length-1; i > -1 ; i--) {
26651             var a = node.attributes[i];
26652             //console.log(a);
26653             
26654             if (a.name.toLowerCase().substr(0,2)=='on')  {
26655                 node.removeAttribute(a.name);
26656                 continue;
26657             }
26658             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26659                 node.removeAttribute(a.name);
26660                 continue;
26661             }
26662             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26663                 cleanAttr(a.name,a.value); // fixme..
26664                 continue;
26665             }
26666             if (a.name == 'style') {
26667                 cleanStyle(a.name,a.value);
26668                 continue;
26669             }
26670             /// clean up MS crap..
26671             // tecnically this should be a list of valid class'es..
26672             
26673             
26674             if (a.name == 'class') {
26675                 if (a.value.match(/^Mso/)) {
26676                     node.removeAttribute('class');
26677                 }
26678                 
26679                 if (a.value.match(/^body$/)) {
26680                     node.removeAttribute('class');
26681                 }
26682                 continue;
26683             }
26684             
26685             // style cleanup!?
26686             // class cleanup?
26687             
26688         }
26689         
26690         
26691         this.cleanUpChildren(node);
26692         
26693         
26694     },
26695     
26696     /**
26697      * Clean up MS wordisms...
26698      */
26699     cleanWord : function(node)
26700     {
26701         if (!node) {
26702             this.cleanWord(this.doc.body);
26703             return;
26704         }
26705         
26706         if(
26707                 node.nodeName == 'SPAN' &&
26708                 !node.hasAttributes() &&
26709                 node.childNodes.length == 1 &&
26710                 node.firstChild.nodeName == "#text"  
26711         ) {
26712             var textNode = node.firstChild;
26713             node.removeChild(textNode);
26714             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26715                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26716             }
26717             node.parentNode.insertBefore(textNode, node);
26718             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26719                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26720             }
26721             node.parentNode.removeChild(node);
26722         }
26723         
26724         if (node.nodeName == "#text") {
26725             // clean up silly Windows -- stuff?
26726             return; 
26727         }
26728         if (node.nodeName == "#comment") {
26729             node.parentNode.removeChild(node);
26730             // clean up silly Windows -- stuff?
26731             return; 
26732         }
26733         
26734         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26735             node.parentNode.removeChild(node);
26736             return;
26737         }
26738         //Roo.log(node.tagName);
26739         // remove - but keep children..
26740         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26741             //Roo.log('-- removed');
26742             while (node.childNodes.length) {
26743                 var cn = node.childNodes[0];
26744                 node.removeChild(cn);
26745                 node.parentNode.insertBefore(cn, node);
26746                 // move node to parent - and clean it..
26747                 this.cleanWord(cn);
26748             }
26749             node.parentNode.removeChild(node);
26750             /// no need to iterate chidlren = it's got none..
26751             //this.iterateChildren(node, this.cleanWord);
26752             return;
26753         }
26754         // clean styles
26755         if (node.className.length) {
26756             
26757             var cn = node.className.split(/\W+/);
26758             var cna = [];
26759             Roo.each(cn, function(cls) {
26760                 if (cls.match(/Mso[a-zA-Z]+/)) {
26761                     return;
26762                 }
26763                 cna.push(cls);
26764             });
26765             node.className = cna.length ? cna.join(' ') : '';
26766             if (!cna.length) {
26767                 node.removeAttribute("class");
26768             }
26769         }
26770         
26771         if (node.hasAttribute("lang")) {
26772             node.removeAttribute("lang");
26773         }
26774         
26775         if (node.hasAttribute("style")) {
26776             
26777             var styles = node.getAttribute("style").split(";");
26778             var nstyle = [];
26779             Roo.each(styles, function(s) {
26780                 if (!s.match(/:/)) {
26781                     return;
26782                 }
26783                 var kv = s.split(":");
26784                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26785                     return;
26786                 }
26787                 // what ever is left... we allow.
26788                 nstyle.push(s);
26789             });
26790             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26791             if (!nstyle.length) {
26792                 node.removeAttribute('style');
26793             }
26794         }
26795         this.iterateChildren(node, this.cleanWord);
26796         
26797         
26798         
26799     },
26800     /**
26801      * iterateChildren of a Node, calling fn each time, using this as the scole..
26802      * @param {DomNode} node node to iterate children of.
26803      * @param {Function} fn method of this class to call on each item.
26804      */
26805     iterateChildren : function(node, fn)
26806     {
26807         if (!node.childNodes.length) {
26808                 return;
26809         }
26810         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26811            fn.call(this, node.childNodes[i])
26812         }
26813     },
26814     
26815     
26816     /**
26817      * cleanTableWidths.
26818      *
26819      * Quite often pasting from word etc.. results in tables with column and widths.
26820      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26821      *
26822      */
26823     cleanTableWidths : function(node)
26824     {
26825          
26826          
26827         if (!node) {
26828             this.cleanTableWidths(this.doc.body);
26829             return;
26830         }
26831         
26832         // ignore list...
26833         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26834             return; 
26835         }
26836         Roo.log(node.tagName);
26837         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26838             this.iterateChildren(node, this.cleanTableWidths);
26839             return;
26840         }
26841         if (node.hasAttribute('width')) {
26842             node.removeAttribute('width');
26843         }
26844         
26845          
26846         if (node.hasAttribute("style")) {
26847             // pretty basic...
26848             
26849             var styles = node.getAttribute("style").split(";");
26850             var nstyle = [];
26851             Roo.each(styles, function(s) {
26852                 if (!s.match(/:/)) {
26853                     return;
26854                 }
26855                 var kv = s.split(":");
26856                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26857                     return;
26858                 }
26859                 // what ever is left... we allow.
26860                 nstyle.push(s);
26861             });
26862             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26863             if (!nstyle.length) {
26864                 node.removeAttribute('style');
26865             }
26866         }
26867         
26868         this.iterateChildren(node, this.cleanTableWidths);
26869         
26870         
26871     },
26872     
26873     
26874     
26875     
26876     domToHTML : function(currentElement, depth, nopadtext) {
26877         
26878         depth = depth || 0;
26879         nopadtext = nopadtext || false;
26880     
26881         if (!currentElement) {
26882             return this.domToHTML(this.doc.body);
26883         }
26884         
26885         //Roo.log(currentElement);
26886         var j;
26887         var allText = false;
26888         var nodeName = currentElement.nodeName;
26889         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26890         
26891         if  (nodeName == '#text') {
26892             
26893             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26894         }
26895         
26896         
26897         var ret = '';
26898         if (nodeName != 'BODY') {
26899              
26900             var i = 0;
26901             // Prints the node tagName, such as <A>, <IMG>, etc
26902             if (tagName) {
26903                 var attr = [];
26904                 for(i = 0; i < currentElement.attributes.length;i++) {
26905                     // quoting?
26906                     var aname = currentElement.attributes.item(i).name;
26907                     if (!currentElement.attributes.item(i).value.length) {
26908                         continue;
26909                     }
26910                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26911                 }
26912                 
26913                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26914             } 
26915             else {
26916                 
26917                 // eack
26918             }
26919         } else {
26920             tagName = false;
26921         }
26922         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26923             return ret;
26924         }
26925         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26926             nopadtext = true;
26927         }
26928         
26929         
26930         // Traverse the tree
26931         i = 0;
26932         var currentElementChild = currentElement.childNodes.item(i);
26933         var allText = true;
26934         var innerHTML  = '';
26935         lastnode = '';
26936         while (currentElementChild) {
26937             // Formatting code (indent the tree so it looks nice on the screen)
26938             var nopad = nopadtext;
26939             if (lastnode == 'SPAN') {
26940                 nopad  = true;
26941             }
26942             // text
26943             if  (currentElementChild.nodeName == '#text') {
26944                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26945                 toadd = nopadtext ? toadd : toadd.trim();
26946                 if (!nopad && toadd.length > 80) {
26947                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26948                 }
26949                 innerHTML  += toadd;
26950                 
26951                 i++;
26952                 currentElementChild = currentElement.childNodes.item(i);
26953                 lastNode = '';
26954                 continue;
26955             }
26956             allText = false;
26957             
26958             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26959                 
26960             // Recursively traverse the tree structure of the child node
26961             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26962             lastnode = currentElementChild.nodeName;
26963             i++;
26964             currentElementChild=currentElement.childNodes.item(i);
26965         }
26966         
26967         ret += innerHTML;
26968         
26969         if (!allText) {
26970                 // The remaining code is mostly for formatting the tree
26971             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26972         }
26973         
26974         
26975         if (tagName) {
26976             ret+= "</"+tagName+">";
26977         }
26978         return ret;
26979         
26980     },
26981         
26982     applyBlacklists : function()
26983     {
26984         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26985         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26986         
26987         this.white = [];
26988         this.black = [];
26989         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26990             if (b.indexOf(tag) > -1) {
26991                 return;
26992             }
26993             this.white.push(tag);
26994             
26995         }, this);
26996         
26997         Roo.each(w, function(tag) {
26998             if (b.indexOf(tag) > -1) {
26999                 return;
27000             }
27001             if (this.white.indexOf(tag) > -1) {
27002                 return;
27003             }
27004             this.white.push(tag);
27005             
27006         }, this);
27007         
27008         
27009         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27010             if (w.indexOf(tag) > -1) {
27011                 return;
27012             }
27013             this.black.push(tag);
27014             
27015         }, this);
27016         
27017         Roo.each(b, function(tag) {
27018             if (w.indexOf(tag) > -1) {
27019                 return;
27020             }
27021             if (this.black.indexOf(tag) > -1) {
27022                 return;
27023             }
27024             this.black.push(tag);
27025             
27026         }, this);
27027         
27028         
27029         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27030         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27031         
27032         this.cwhite = [];
27033         this.cblack = [];
27034         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27035             if (b.indexOf(tag) > -1) {
27036                 return;
27037             }
27038             this.cwhite.push(tag);
27039             
27040         }, this);
27041         
27042         Roo.each(w, function(tag) {
27043             if (b.indexOf(tag) > -1) {
27044                 return;
27045             }
27046             if (this.cwhite.indexOf(tag) > -1) {
27047                 return;
27048             }
27049             this.cwhite.push(tag);
27050             
27051         }, this);
27052         
27053         
27054         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27055             if (w.indexOf(tag) > -1) {
27056                 return;
27057             }
27058             this.cblack.push(tag);
27059             
27060         }, this);
27061         
27062         Roo.each(b, function(tag) {
27063             if (w.indexOf(tag) > -1) {
27064                 return;
27065             }
27066             if (this.cblack.indexOf(tag) > -1) {
27067                 return;
27068             }
27069             this.cblack.push(tag);
27070             
27071         }, this);
27072     },
27073     
27074     setStylesheets : function(stylesheets)
27075     {
27076         if(typeof(stylesheets) == 'string'){
27077             Roo.get(this.iframe.contentDocument.head).createChild({
27078                 tag : 'link',
27079                 rel : 'stylesheet',
27080                 type : 'text/css',
27081                 href : stylesheets
27082             });
27083             
27084             return;
27085         }
27086         var _this = this;
27087      
27088         Roo.each(stylesheets, function(s) {
27089             if(!s.length){
27090                 return;
27091             }
27092             
27093             Roo.get(_this.iframe.contentDocument.head).createChild({
27094                 tag : 'link',
27095                 rel : 'stylesheet',
27096                 type : 'text/css',
27097                 href : s
27098             });
27099         });
27100
27101         
27102     },
27103     
27104     removeStylesheets : function()
27105     {
27106         var _this = this;
27107         
27108         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27109             s.remove();
27110         });
27111     },
27112     
27113     setStyle : function(style)
27114     {
27115         Roo.get(this.iframe.contentDocument.head).createChild({
27116             tag : 'style',
27117             type : 'text/css',
27118             html : style
27119         });
27120
27121         return;
27122     }
27123     
27124     // hide stuff that is not compatible
27125     /**
27126      * @event blur
27127      * @hide
27128      */
27129     /**
27130      * @event change
27131      * @hide
27132      */
27133     /**
27134      * @event focus
27135      * @hide
27136      */
27137     /**
27138      * @event specialkey
27139      * @hide
27140      */
27141     /**
27142      * @cfg {String} fieldClass @hide
27143      */
27144     /**
27145      * @cfg {String} focusClass @hide
27146      */
27147     /**
27148      * @cfg {String} autoCreate @hide
27149      */
27150     /**
27151      * @cfg {String} inputType @hide
27152      */
27153     /**
27154      * @cfg {String} invalidClass @hide
27155      */
27156     /**
27157      * @cfg {String} invalidText @hide
27158      */
27159     /**
27160      * @cfg {String} msgFx @hide
27161      */
27162     /**
27163      * @cfg {String} validateOnBlur @hide
27164      */
27165 });
27166
27167 Roo.HtmlEditorCore.white = [
27168         'area', 'br', 'img', 'input', 'hr', 'wbr',
27169         
27170        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27171        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27172        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27173        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27174        'table',   'ul',         'xmp', 
27175        
27176        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27177       'thead',   'tr', 
27178      
27179       'dir', 'menu', 'ol', 'ul', 'dl',
27180        
27181       'embed',  'object'
27182 ];
27183
27184
27185 Roo.HtmlEditorCore.black = [
27186     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27187         'applet', // 
27188         'base',   'basefont', 'bgsound', 'blink',  'body', 
27189         'frame',  'frameset', 'head',    'html',   'ilayer', 
27190         'iframe', 'layer',  'link',     'meta',    'object',   
27191         'script', 'style' ,'title',  'xml' // clean later..
27192 ];
27193 Roo.HtmlEditorCore.clean = [
27194     'script', 'style', 'title', 'xml'
27195 ];
27196 Roo.HtmlEditorCore.remove = [
27197     'font'
27198 ];
27199 // attributes..
27200
27201 Roo.HtmlEditorCore.ablack = [
27202     'on'
27203 ];
27204     
27205 Roo.HtmlEditorCore.aclean = [ 
27206     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27207 ];
27208
27209 // protocols..
27210 Roo.HtmlEditorCore.pwhite= [
27211         'http',  'https',  'mailto'
27212 ];
27213
27214 // white listed style attributes.
27215 Roo.HtmlEditorCore.cwhite= [
27216       //  'text-align', /// default is to allow most things..
27217       
27218          
27219 //        'font-size'//??
27220 ];
27221
27222 // black listed style attributes.
27223 Roo.HtmlEditorCore.cblack= [
27224       //  'font-size' -- this can be set by the project 
27225 ];
27226
27227
27228 Roo.HtmlEditorCore.swapCodes   =[ 
27229     [    8211, "&#8211;" ], 
27230     [    8212, "&#8212;" ], 
27231     [    8216,  "'" ],  
27232     [    8217, "'" ],  
27233     [    8220, '"' ],  
27234     [    8221, '"' ],  
27235     [    8226, "*" ],  
27236     [    8230, "..." ]
27237 ]; 
27238
27239     /*
27240  * - LGPL
27241  *
27242  * HtmlEditor
27243  * 
27244  */
27245
27246 /**
27247  * @class Roo.bootstrap.HtmlEditor
27248  * @extends Roo.bootstrap.TextArea
27249  * Bootstrap HtmlEditor class
27250
27251  * @constructor
27252  * Create a new HtmlEditor
27253  * @param {Object} config The config object
27254  */
27255
27256 Roo.bootstrap.HtmlEditor = function(config){
27257     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27258     if (!this.toolbars) {
27259         this.toolbars = [];
27260     }
27261     
27262     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27263     this.addEvents({
27264             /**
27265              * @event initialize
27266              * Fires when the editor is fully initialized (including the iframe)
27267              * @param {HtmlEditor} this
27268              */
27269             initialize: true,
27270             /**
27271              * @event activate
27272              * Fires when the editor is first receives the focus. Any insertion must wait
27273              * until after this event.
27274              * @param {HtmlEditor} this
27275              */
27276             activate: true,
27277              /**
27278              * @event beforesync
27279              * Fires before the textarea is updated with content from the editor iframe. Return false
27280              * to cancel the sync.
27281              * @param {HtmlEditor} this
27282              * @param {String} html
27283              */
27284             beforesync: true,
27285              /**
27286              * @event beforepush
27287              * Fires before the iframe editor is updated with content from the textarea. Return false
27288              * to cancel the push.
27289              * @param {HtmlEditor} this
27290              * @param {String} html
27291              */
27292             beforepush: true,
27293              /**
27294              * @event sync
27295              * Fires when the textarea is updated with content from the editor iframe.
27296              * @param {HtmlEditor} this
27297              * @param {String} html
27298              */
27299             sync: true,
27300              /**
27301              * @event push
27302              * Fires when the iframe editor is updated with content from the textarea.
27303              * @param {HtmlEditor} this
27304              * @param {String} html
27305              */
27306             push: true,
27307              /**
27308              * @event editmodechange
27309              * Fires when the editor switches edit modes
27310              * @param {HtmlEditor} this
27311              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27312              */
27313             editmodechange: true,
27314             /**
27315              * @event editorevent
27316              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27317              * @param {HtmlEditor} this
27318              */
27319             editorevent: true,
27320             /**
27321              * @event firstfocus
27322              * Fires when on first focus - needed by toolbars..
27323              * @param {HtmlEditor} this
27324              */
27325             firstfocus: true,
27326             /**
27327              * @event autosave
27328              * Auto save the htmlEditor value as a file into Events
27329              * @param {HtmlEditor} this
27330              */
27331             autosave: true,
27332             /**
27333              * @event savedpreview
27334              * preview the saved version of htmlEditor
27335              * @param {HtmlEditor} this
27336              */
27337             savedpreview: true
27338         });
27339 };
27340
27341
27342 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27343     
27344     
27345       /**
27346      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27347      */
27348     toolbars : false,
27349     
27350      /**
27351     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27352     */
27353     btns : [],
27354    
27355      /**
27356      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27357      *                        Roo.resizable.
27358      */
27359     resizable : false,
27360      /**
27361      * @cfg {Number} height (in pixels)
27362      */   
27363     height: 300,
27364    /**
27365      * @cfg {Number} width (in pixels)
27366      */   
27367     width: false,
27368     
27369     /**
27370      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27371      * 
27372      */
27373     stylesheets: false,
27374     
27375     // id of frame..
27376     frameId: false,
27377     
27378     // private properties
27379     validationEvent : false,
27380     deferHeight: true,
27381     initialized : false,
27382     activated : false,
27383     
27384     onFocus : Roo.emptyFn,
27385     iframePad:3,
27386     hideMode:'offsets',
27387     
27388     tbContainer : false,
27389     
27390     bodyCls : '',
27391     
27392     toolbarContainer :function() {
27393         return this.wrap.select('.x-html-editor-tb',true).first();
27394     },
27395
27396     /**
27397      * Protected method that will not generally be called directly. It
27398      * is called when the editor creates its toolbar. Override this method if you need to
27399      * add custom toolbar buttons.
27400      * @param {HtmlEditor} editor
27401      */
27402     createToolbar : function(){
27403         Roo.log('renewing');
27404         Roo.log("create toolbars");
27405         
27406         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27407         this.toolbars[0].render(this.toolbarContainer());
27408         
27409         return;
27410         
27411 //        if (!editor.toolbars || !editor.toolbars.length) {
27412 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27413 //        }
27414 //        
27415 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27416 //            editor.toolbars[i] = Roo.factory(
27417 //                    typeof(editor.toolbars[i]) == 'string' ?
27418 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27419 //                Roo.bootstrap.HtmlEditor);
27420 //            editor.toolbars[i].init(editor);
27421 //        }
27422     },
27423
27424      
27425     // private
27426     onRender : function(ct, position)
27427     {
27428        // Roo.log("Call onRender: " + this.xtype);
27429         var _t = this;
27430         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27431       
27432         this.wrap = this.inputEl().wrap({
27433             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27434         });
27435         
27436         this.editorcore.onRender(ct, position);
27437          
27438         if (this.resizable) {
27439             this.resizeEl = new Roo.Resizable(this.wrap, {
27440                 pinned : true,
27441                 wrap: true,
27442                 dynamic : true,
27443                 minHeight : this.height,
27444                 height: this.height,
27445                 handles : this.resizable,
27446                 width: this.width,
27447                 listeners : {
27448                     resize : function(r, w, h) {
27449                         _t.onResize(w,h); // -something
27450                     }
27451                 }
27452             });
27453             
27454         }
27455         this.createToolbar(this);
27456        
27457         
27458         if(!this.width && this.resizable){
27459             this.setSize(this.wrap.getSize());
27460         }
27461         if (this.resizeEl) {
27462             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27463             // should trigger onReize..
27464         }
27465         
27466     },
27467
27468     // private
27469     onResize : function(w, h)
27470     {
27471         Roo.log('resize: ' +w + ',' + h );
27472         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27473         var ew = false;
27474         var eh = false;
27475         
27476         if(this.inputEl() ){
27477             if(typeof w == 'number'){
27478                 var aw = w - this.wrap.getFrameWidth('lr');
27479                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27480                 ew = aw;
27481             }
27482             if(typeof h == 'number'){
27483                  var tbh = -11;  // fixme it needs to tool bar size!
27484                 for (var i =0; i < this.toolbars.length;i++) {
27485                     // fixme - ask toolbars for heights?
27486                     tbh += this.toolbars[i].el.getHeight();
27487                     //if (this.toolbars[i].footer) {
27488                     //    tbh += this.toolbars[i].footer.el.getHeight();
27489                     //}
27490                 }
27491               
27492                 
27493                 
27494                 
27495                 
27496                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27497                 ah -= 5; // knock a few pixes off for look..
27498                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27499                 var eh = ah;
27500             }
27501         }
27502         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27503         this.editorcore.onResize(ew,eh);
27504         
27505     },
27506
27507     /**
27508      * Toggles the editor between standard and source edit mode.
27509      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27510      */
27511     toggleSourceEdit : function(sourceEditMode)
27512     {
27513         this.editorcore.toggleSourceEdit(sourceEditMode);
27514         
27515         if(this.editorcore.sourceEditMode){
27516             Roo.log('editor - showing textarea');
27517             
27518 //            Roo.log('in');
27519 //            Roo.log(this.syncValue());
27520             this.syncValue();
27521             this.inputEl().removeClass(['hide', 'x-hidden']);
27522             this.inputEl().dom.removeAttribute('tabIndex');
27523             this.inputEl().focus();
27524         }else{
27525             Roo.log('editor - hiding textarea');
27526 //            Roo.log('out')
27527 //            Roo.log(this.pushValue()); 
27528             this.pushValue();
27529             
27530             this.inputEl().addClass(['hide', 'x-hidden']);
27531             this.inputEl().dom.setAttribute('tabIndex', -1);
27532             //this.deferFocus();
27533         }
27534          
27535         if(this.resizable){
27536             this.setSize(this.wrap.getSize());
27537         }
27538         
27539         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27540     },
27541  
27542     // private (for BoxComponent)
27543     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27544
27545     // private (for BoxComponent)
27546     getResizeEl : function(){
27547         return this.wrap;
27548     },
27549
27550     // private (for BoxComponent)
27551     getPositionEl : function(){
27552         return this.wrap;
27553     },
27554
27555     // private
27556     initEvents : function(){
27557         this.originalValue = this.getValue();
27558     },
27559
27560 //    /**
27561 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27562 //     * @method
27563 //     */
27564 //    markInvalid : Roo.emptyFn,
27565 //    /**
27566 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27567 //     * @method
27568 //     */
27569 //    clearInvalid : Roo.emptyFn,
27570
27571     setValue : function(v){
27572         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27573         this.editorcore.pushValue();
27574     },
27575
27576      
27577     // private
27578     deferFocus : function(){
27579         this.focus.defer(10, this);
27580     },
27581
27582     // doc'ed in Field
27583     focus : function(){
27584         this.editorcore.focus();
27585         
27586     },
27587       
27588
27589     // private
27590     onDestroy : function(){
27591         
27592         
27593         
27594         if(this.rendered){
27595             
27596             for (var i =0; i < this.toolbars.length;i++) {
27597                 // fixme - ask toolbars for heights?
27598                 this.toolbars[i].onDestroy();
27599             }
27600             
27601             this.wrap.dom.innerHTML = '';
27602             this.wrap.remove();
27603         }
27604     },
27605
27606     // private
27607     onFirstFocus : function(){
27608         //Roo.log("onFirstFocus");
27609         this.editorcore.onFirstFocus();
27610          for (var i =0; i < this.toolbars.length;i++) {
27611             this.toolbars[i].onFirstFocus();
27612         }
27613         
27614     },
27615     
27616     // private
27617     syncValue : function()
27618     {   
27619         this.editorcore.syncValue();
27620     },
27621     
27622     pushValue : function()
27623     {   
27624         this.editorcore.pushValue();
27625     }
27626      
27627     
27628     // hide stuff that is not compatible
27629     /**
27630      * @event blur
27631      * @hide
27632      */
27633     /**
27634      * @event change
27635      * @hide
27636      */
27637     /**
27638      * @event focus
27639      * @hide
27640      */
27641     /**
27642      * @event specialkey
27643      * @hide
27644      */
27645     /**
27646      * @cfg {String} fieldClass @hide
27647      */
27648     /**
27649      * @cfg {String} focusClass @hide
27650      */
27651     /**
27652      * @cfg {String} autoCreate @hide
27653      */
27654     /**
27655      * @cfg {String} inputType @hide
27656      */
27657      
27658     /**
27659      * @cfg {String} invalidText @hide
27660      */
27661     /**
27662      * @cfg {String} msgFx @hide
27663      */
27664     /**
27665      * @cfg {String} validateOnBlur @hide
27666      */
27667 });
27668  
27669     
27670    
27671    
27672    
27673       
27674 Roo.namespace('Roo.bootstrap.htmleditor');
27675 /**
27676  * @class Roo.bootstrap.HtmlEditorToolbar1
27677  * Basic Toolbar
27678  * 
27679  * @example
27680  * Usage:
27681  *
27682  new Roo.bootstrap.HtmlEditor({
27683     ....
27684     toolbars : [
27685         new Roo.bootstrap.HtmlEditorToolbar1({
27686             disable : { fonts: 1 , format: 1, ..., ... , ...],
27687             btns : [ .... ]
27688         })
27689     }
27690      
27691  * 
27692  * @cfg {Object} disable List of elements to disable..
27693  * @cfg {Array} btns List of additional buttons.
27694  * 
27695  * 
27696  * NEEDS Extra CSS? 
27697  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27698  */
27699  
27700 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27701 {
27702     
27703     Roo.apply(this, config);
27704     
27705     // default disabled, based on 'good practice'..
27706     this.disable = this.disable || {};
27707     Roo.applyIf(this.disable, {
27708         fontSize : true,
27709         colors : true,
27710         specialElements : true
27711     });
27712     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27713     
27714     this.editor = config.editor;
27715     this.editorcore = config.editor.editorcore;
27716     
27717     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27718     
27719     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27720     // dont call parent... till later.
27721 }
27722 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27723      
27724     bar : true,
27725     
27726     editor : false,
27727     editorcore : false,
27728     
27729     
27730     formats : [
27731         "p" ,  
27732         "h1","h2","h3","h4","h5","h6", 
27733         "pre", "code", 
27734         "abbr", "acronym", "address", "cite", "samp", "var",
27735         'div','span'
27736     ],
27737     
27738     onRender : function(ct, position)
27739     {
27740        // Roo.log("Call onRender: " + this.xtype);
27741         
27742        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27743        Roo.log(this.el);
27744        this.el.dom.style.marginBottom = '0';
27745        var _this = this;
27746        var editorcore = this.editorcore;
27747        var editor= this.editor;
27748        
27749        var children = [];
27750        var btn = function(id,cmd , toggle, handler, html){
27751        
27752             var  event = toggle ? 'toggle' : 'click';
27753        
27754             var a = {
27755                 size : 'sm',
27756                 xtype: 'Button',
27757                 xns: Roo.bootstrap,
27758                 //glyphicon : id,
27759                 fa: id,
27760                 cmd : id || cmd,
27761                 enableToggle:toggle !== false,
27762                 html : html || '',
27763                 pressed : toggle ? false : null,
27764                 listeners : {}
27765             };
27766             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27767                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27768             };
27769             children.push(a);
27770             return a;
27771        }
27772        
27773     //    var cb_box = function...
27774         
27775         var style = {
27776                 xtype: 'Button',
27777                 size : 'sm',
27778                 xns: Roo.bootstrap,
27779                 fa : 'font',
27780                 //html : 'submit'
27781                 menu : {
27782                     xtype: 'Menu',
27783                     xns: Roo.bootstrap,
27784                     items:  []
27785                 }
27786         };
27787         Roo.each(this.formats, function(f) {
27788             style.menu.items.push({
27789                 xtype :'MenuItem',
27790                 xns: Roo.bootstrap,
27791                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27792                 tagname : f,
27793                 listeners : {
27794                     click : function()
27795                     {
27796                         editorcore.insertTag(this.tagname);
27797                         editor.focus();
27798                     }
27799                 }
27800                 
27801             });
27802         });
27803         children.push(style);   
27804         
27805         btn('bold',false,true);
27806         btn('italic',false,true);
27807         btn('align-left', 'justifyleft',true);
27808         btn('align-center', 'justifycenter',true);
27809         btn('align-right' , 'justifyright',true);
27810         btn('link', false, false, function(btn) {
27811             //Roo.log("create link?");
27812             var url = prompt(this.createLinkText, this.defaultLinkValue);
27813             if(url && url != 'http:/'+'/'){
27814                 this.editorcore.relayCmd('createlink', url);
27815             }
27816         }),
27817         btn('list','insertunorderedlist',true);
27818         btn('pencil', false,true, function(btn){
27819                 Roo.log(this);
27820                 this.toggleSourceEdit(btn.pressed);
27821         });
27822         
27823         if (this.editor.btns.length > 0) {
27824             for (var i = 0; i<this.editor.btns.length; i++) {
27825                 children.push(this.editor.btns[i]);
27826             }
27827         }
27828         
27829         /*
27830         var cog = {
27831                 xtype: 'Button',
27832                 size : 'sm',
27833                 xns: Roo.bootstrap,
27834                 glyphicon : 'cog',
27835                 //html : 'submit'
27836                 menu : {
27837                     xtype: 'Menu',
27838                     xns: Roo.bootstrap,
27839                     items:  []
27840                 }
27841         };
27842         
27843         cog.menu.items.push({
27844             xtype :'MenuItem',
27845             xns: Roo.bootstrap,
27846             html : Clean styles,
27847             tagname : f,
27848             listeners : {
27849                 click : function()
27850                 {
27851                     editorcore.insertTag(this.tagname);
27852                     editor.focus();
27853                 }
27854             }
27855             
27856         });
27857        */
27858         
27859          
27860        this.xtype = 'NavSimplebar';
27861         
27862         for(var i=0;i< children.length;i++) {
27863             
27864             this.buttons.add(this.addxtypeChild(children[i]));
27865             
27866         }
27867         
27868         editor.on('editorevent', this.updateToolbar, this);
27869     },
27870     onBtnClick : function(id)
27871     {
27872        this.editorcore.relayCmd(id);
27873        this.editorcore.focus();
27874     },
27875     
27876     /**
27877      * Protected method that will not generally be called directly. It triggers
27878      * a toolbar update by reading the markup state of the current selection in the editor.
27879      */
27880     updateToolbar: function(){
27881
27882         if(!this.editorcore.activated){
27883             this.editor.onFirstFocus(); // is this neeed?
27884             return;
27885         }
27886
27887         var btns = this.buttons; 
27888         var doc = this.editorcore.doc;
27889         btns.get('bold').setActive(doc.queryCommandState('bold'));
27890         btns.get('italic').setActive(doc.queryCommandState('italic'));
27891         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27892         
27893         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27894         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27895         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27896         
27897         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27898         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27899          /*
27900         
27901         var ans = this.editorcore.getAllAncestors();
27902         if (this.formatCombo) {
27903             
27904             
27905             var store = this.formatCombo.store;
27906             this.formatCombo.setValue("");
27907             for (var i =0; i < ans.length;i++) {
27908                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27909                     // select it..
27910                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27911                     break;
27912                 }
27913             }
27914         }
27915         
27916         
27917         
27918         // hides menus... - so this cant be on a menu...
27919         Roo.bootstrap.MenuMgr.hideAll();
27920         */
27921         Roo.bootstrap.MenuMgr.hideAll();
27922         //this.editorsyncValue();
27923     },
27924     onFirstFocus: function() {
27925         this.buttons.each(function(item){
27926            item.enable();
27927         });
27928     },
27929     toggleSourceEdit : function(sourceEditMode){
27930         
27931           
27932         if(sourceEditMode){
27933             Roo.log("disabling buttons");
27934            this.buttons.each( function(item){
27935                 if(item.cmd != 'pencil'){
27936                     item.disable();
27937                 }
27938             });
27939           
27940         }else{
27941             Roo.log("enabling buttons");
27942             if(this.editorcore.initialized){
27943                 this.buttons.each( function(item){
27944                     item.enable();
27945                 });
27946             }
27947             
27948         }
27949         Roo.log("calling toggole on editor");
27950         // tell the editor that it's been pressed..
27951         this.editor.toggleSourceEdit(sourceEditMode);
27952        
27953     }
27954 });
27955
27956
27957
27958
27959  
27960 /*
27961  * - LGPL
27962  */
27963
27964 /**
27965  * @class Roo.bootstrap.Markdown
27966  * @extends Roo.bootstrap.TextArea
27967  * Bootstrap Showdown editable area
27968  * @cfg {string} content
27969  * 
27970  * @constructor
27971  * Create a new Showdown
27972  */
27973
27974 Roo.bootstrap.Markdown = function(config){
27975     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27976    
27977 };
27978
27979 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27980     
27981     editing :false,
27982     
27983     initEvents : function()
27984     {
27985         
27986         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27987         this.markdownEl = this.el.createChild({
27988             cls : 'roo-markdown-area'
27989         });
27990         this.inputEl().addClass('d-none');
27991         if (this.getValue() == '') {
27992             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27993             
27994         } else {
27995             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27996         }
27997         this.markdownEl.on('click', this.toggleTextEdit, this);
27998         this.on('blur', this.toggleTextEdit, this);
27999         this.on('specialkey', this.resizeTextArea, this);
28000     },
28001     
28002     toggleTextEdit : function()
28003     {
28004         var sh = this.markdownEl.getHeight();
28005         this.inputEl().addClass('d-none');
28006         this.markdownEl.addClass('d-none');
28007         if (!this.editing) {
28008             // show editor?
28009             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28010             this.inputEl().removeClass('d-none');
28011             this.inputEl().focus();
28012             this.editing = true;
28013             return;
28014         }
28015         // show showdown...
28016         this.updateMarkdown();
28017         this.markdownEl.removeClass('d-none');
28018         this.editing = false;
28019         return;
28020     },
28021     updateMarkdown : function()
28022     {
28023         if (this.getValue() == '') {
28024             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28025             return;
28026         }
28027  
28028         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28029     },
28030     
28031     resizeTextArea: function () {
28032         
28033         var sh = 100;
28034         Roo.log([sh, this.getValue().split("\n").length * 30]);
28035         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28036     },
28037     setValue : function(val)
28038     {
28039         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28040         if (!this.editing) {
28041             this.updateMarkdown();
28042         }
28043         
28044     },
28045     focus : function()
28046     {
28047         if (!this.editing) {
28048             this.toggleTextEdit();
28049         }
28050         
28051     }
28052
28053
28054 });/*
28055  * Based on:
28056  * Ext JS Library 1.1.1
28057  * Copyright(c) 2006-2007, Ext JS, LLC.
28058  *
28059  * Originally Released Under LGPL - original licence link has changed is not relivant.
28060  *
28061  * Fork - LGPL
28062  * <script type="text/javascript">
28063  */
28064  
28065 /**
28066  * @class Roo.bootstrap.PagingToolbar
28067  * @extends Roo.bootstrap.NavSimplebar
28068  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28069  * @constructor
28070  * Create a new PagingToolbar
28071  * @param {Object} config The config object
28072  * @param {Roo.data.Store} store
28073  */
28074 Roo.bootstrap.PagingToolbar = function(config)
28075 {
28076     // old args format still supported... - xtype is prefered..
28077         // created from xtype...
28078     
28079     this.ds = config.dataSource;
28080     
28081     if (config.store && !this.ds) {
28082         this.store= Roo.factory(config.store, Roo.data);
28083         this.ds = this.store;
28084         this.ds.xmodule = this.xmodule || false;
28085     }
28086     
28087     this.toolbarItems = [];
28088     if (config.items) {
28089         this.toolbarItems = config.items;
28090     }
28091     
28092     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28093     
28094     this.cursor = 0;
28095     
28096     if (this.ds) { 
28097         this.bind(this.ds);
28098     }
28099     
28100     if (Roo.bootstrap.version == 4) {
28101         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28102     } else {
28103         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28104     }
28105     
28106 };
28107
28108 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28109     /**
28110      * @cfg {Roo.data.Store} dataSource
28111      * The underlying data store providing the paged data
28112      */
28113     /**
28114      * @cfg {String/HTMLElement/Element} container
28115      * container The id or element that will contain the toolbar
28116      */
28117     /**
28118      * @cfg {Boolean} displayInfo
28119      * True to display the displayMsg (defaults to false)
28120      */
28121     /**
28122      * @cfg {Number} pageSize
28123      * The number of records to display per page (defaults to 20)
28124      */
28125     pageSize: 20,
28126     /**
28127      * @cfg {String} displayMsg
28128      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28129      */
28130     displayMsg : 'Displaying {0} - {1} of {2}',
28131     /**
28132      * @cfg {String} emptyMsg
28133      * The message to display when no records are found (defaults to "No data to display")
28134      */
28135     emptyMsg : 'No data to display',
28136     /**
28137      * Customizable piece of the default paging text (defaults to "Page")
28138      * @type String
28139      */
28140     beforePageText : "Page",
28141     /**
28142      * Customizable piece of the default paging text (defaults to "of %0")
28143      * @type String
28144      */
28145     afterPageText : "of {0}",
28146     /**
28147      * Customizable piece of the default paging text (defaults to "First Page")
28148      * @type String
28149      */
28150     firstText : "First Page",
28151     /**
28152      * Customizable piece of the default paging text (defaults to "Previous Page")
28153      * @type String
28154      */
28155     prevText : "Previous Page",
28156     /**
28157      * Customizable piece of the default paging text (defaults to "Next Page")
28158      * @type String
28159      */
28160     nextText : "Next Page",
28161     /**
28162      * Customizable piece of the default paging text (defaults to "Last Page")
28163      * @type String
28164      */
28165     lastText : "Last Page",
28166     /**
28167      * Customizable piece of the default paging text (defaults to "Refresh")
28168      * @type String
28169      */
28170     refreshText : "Refresh",
28171
28172     buttons : false,
28173     // private
28174     onRender : function(ct, position) 
28175     {
28176         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28177         this.navgroup.parentId = this.id;
28178         this.navgroup.onRender(this.el, null);
28179         // add the buttons to the navgroup
28180         
28181         if(this.displayInfo){
28182             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28183             this.displayEl = this.el.select('.x-paging-info', true).first();
28184 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28185 //            this.displayEl = navel.el.select('span',true).first();
28186         }
28187         
28188         var _this = this;
28189         
28190         if(this.buttons){
28191             Roo.each(_this.buttons, function(e){ // this might need to use render????
28192                Roo.factory(e).render(_this.el);
28193             });
28194         }
28195             
28196         Roo.each(_this.toolbarItems, function(e) {
28197             _this.navgroup.addItem(e);
28198         });
28199         
28200         
28201         this.first = this.navgroup.addItem({
28202             tooltip: this.firstText,
28203             cls: "prev btn-outline-secondary",
28204             html : ' <i class="fa fa-step-backward"></i>',
28205             disabled: true,
28206             preventDefault: true,
28207             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28208         });
28209         
28210         this.prev =  this.navgroup.addItem({
28211             tooltip: this.prevText,
28212             cls: "prev btn-outline-secondary",
28213             html : ' <i class="fa fa-backward"></i>',
28214             disabled: true,
28215             preventDefault: true,
28216             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28217         });
28218     //this.addSeparator();
28219         
28220         
28221         var field = this.navgroup.addItem( {
28222             tagtype : 'span',
28223             cls : 'x-paging-position  btn-outline-secondary',
28224              disabled: true,
28225             html : this.beforePageText  +
28226                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28227                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28228          } ); //?? escaped?
28229         
28230         this.field = field.el.select('input', true).first();
28231         this.field.on("keydown", this.onPagingKeydown, this);
28232         this.field.on("focus", function(){this.dom.select();});
28233     
28234     
28235         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28236         //this.field.setHeight(18);
28237         //this.addSeparator();
28238         this.next = this.navgroup.addItem({
28239             tooltip: this.nextText,
28240             cls: "next btn-outline-secondary",
28241             html : ' <i class="fa fa-forward"></i>',
28242             disabled: true,
28243             preventDefault: true,
28244             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28245         });
28246         this.last = this.navgroup.addItem({
28247             tooltip: this.lastText,
28248             html : ' <i class="fa fa-step-forward"></i>',
28249             cls: "next btn-outline-secondary",
28250             disabled: true,
28251             preventDefault: true,
28252             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28253         });
28254     //this.addSeparator();
28255         this.loading = this.navgroup.addItem({
28256             tooltip: this.refreshText,
28257             cls: "btn-outline-secondary",
28258             html : ' <i class="fa fa-refresh"></i>',
28259             preventDefault: true,
28260             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28261         });
28262         
28263     },
28264
28265     // private
28266     updateInfo : function(){
28267         if(this.displayEl){
28268             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28269             var msg = count == 0 ?
28270                 this.emptyMsg :
28271                 String.format(
28272                     this.displayMsg,
28273                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28274                 );
28275             this.displayEl.update(msg);
28276         }
28277     },
28278
28279     // private
28280     onLoad : function(ds, r, o)
28281     {
28282         this.cursor = o.params && o.params.start ? o.params.start : 0;
28283         
28284         var d = this.getPageData(),
28285             ap = d.activePage,
28286             ps = d.pages;
28287         
28288         
28289         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28290         this.field.dom.value = ap;
28291         this.first.setDisabled(ap == 1);
28292         this.prev.setDisabled(ap == 1);
28293         this.next.setDisabled(ap == ps);
28294         this.last.setDisabled(ap == ps);
28295         this.loading.enable();
28296         this.updateInfo();
28297     },
28298
28299     // private
28300     getPageData : function(){
28301         var total = this.ds.getTotalCount();
28302         return {
28303             total : total,
28304             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28305             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28306         };
28307     },
28308
28309     // private
28310     onLoadError : function(){
28311         this.loading.enable();
28312     },
28313
28314     // private
28315     onPagingKeydown : function(e){
28316         var k = e.getKey();
28317         var d = this.getPageData();
28318         if(k == e.RETURN){
28319             var v = this.field.dom.value, pageNum;
28320             if(!v || isNaN(pageNum = parseInt(v, 10))){
28321                 this.field.dom.value = d.activePage;
28322                 return;
28323             }
28324             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28325             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28326             e.stopEvent();
28327         }
28328         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))
28329         {
28330           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28331           this.field.dom.value = pageNum;
28332           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28333           e.stopEvent();
28334         }
28335         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28336         {
28337           var v = this.field.dom.value, pageNum; 
28338           var increment = (e.shiftKey) ? 10 : 1;
28339           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28340                 increment *= -1;
28341           }
28342           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28343             this.field.dom.value = d.activePage;
28344             return;
28345           }
28346           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28347           {
28348             this.field.dom.value = parseInt(v, 10) + increment;
28349             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28350             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28351           }
28352           e.stopEvent();
28353         }
28354     },
28355
28356     // private
28357     beforeLoad : function(){
28358         if(this.loading){
28359             this.loading.disable();
28360         }
28361     },
28362
28363     // private
28364     onClick : function(which){
28365         
28366         var ds = this.ds;
28367         if (!ds) {
28368             return;
28369         }
28370         
28371         switch(which){
28372             case "first":
28373                 ds.load({params:{start: 0, limit: this.pageSize}});
28374             break;
28375             case "prev":
28376                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28377             break;
28378             case "next":
28379                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28380             break;
28381             case "last":
28382                 var total = ds.getTotalCount();
28383                 var extra = total % this.pageSize;
28384                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28385                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28386             break;
28387             case "refresh":
28388                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28389             break;
28390         }
28391     },
28392
28393     /**
28394      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28395      * @param {Roo.data.Store} store The data store to unbind
28396      */
28397     unbind : function(ds){
28398         ds.un("beforeload", this.beforeLoad, this);
28399         ds.un("load", this.onLoad, this);
28400         ds.un("loadexception", this.onLoadError, this);
28401         ds.un("remove", this.updateInfo, this);
28402         ds.un("add", this.updateInfo, this);
28403         this.ds = undefined;
28404     },
28405
28406     /**
28407      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28408      * @param {Roo.data.Store} store The data store to bind
28409      */
28410     bind : function(ds){
28411         ds.on("beforeload", this.beforeLoad, this);
28412         ds.on("load", this.onLoad, this);
28413         ds.on("loadexception", this.onLoadError, this);
28414         ds.on("remove", this.updateInfo, this);
28415         ds.on("add", this.updateInfo, this);
28416         this.ds = ds;
28417     }
28418 });/*
28419  * - LGPL
28420  *
28421  * element
28422  * 
28423  */
28424
28425 /**
28426  * @class Roo.bootstrap.MessageBar
28427  * @extends Roo.bootstrap.Component
28428  * Bootstrap MessageBar class
28429  * @cfg {String} html contents of the MessageBar
28430  * @cfg {String} weight (info | success | warning | danger) default info
28431  * @cfg {String} beforeClass insert the bar before the given class
28432  * @cfg {Boolean} closable (true | false) default false
28433  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28434  * 
28435  * @constructor
28436  * Create a new Element
28437  * @param {Object} config The config object
28438  */
28439
28440 Roo.bootstrap.MessageBar = function(config){
28441     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28442 };
28443
28444 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28445     
28446     html: '',
28447     weight: 'info',
28448     closable: false,
28449     fixed: false,
28450     beforeClass: 'bootstrap-sticky-wrap',
28451     
28452     getAutoCreate : function(){
28453         
28454         var cfg = {
28455             tag: 'div',
28456             cls: 'alert alert-dismissable alert-' + this.weight,
28457             cn: [
28458                 {
28459                     tag: 'span',
28460                     cls: 'message',
28461                     html: this.html || ''
28462                 }
28463             ]
28464         };
28465         
28466         if(this.fixed){
28467             cfg.cls += ' alert-messages-fixed';
28468         }
28469         
28470         if(this.closable){
28471             cfg.cn.push({
28472                 tag: 'button',
28473                 cls: 'close',
28474                 html: 'x'
28475             });
28476         }
28477         
28478         return cfg;
28479     },
28480     
28481     onRender : function(ct, position)
28482     {
28483         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28484         
28485         if(!this.el){
28486             var cfg = Roo.apply({},  this.getAutoCreate());
28487             cfg.id = Roo.id();
28488             
28489             if (this.cls) {
28490                 cfg.cls += ' ' + this.cls;
28491             }
28492             if (this.style) {
28493                 cfg.style = this.style;
28494             }
28495             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28496             
28497             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28498         }
28499         
28500         this.el.select('>button.close').on('click', this.hide, this);
28501         
28502     },
28503     
28504     show : function()
28505     {
28506         if (!this.rendered) {
28507             this.render();
28508         }
28509         
28510         this.el.show();
28511         
28512         this.fireEvent('show', this);
28513         
28514     },
28515     
28516     hide : function()
28517     {
28518         if (!this.rendered) {
28519             this.render();
28520         }
28521         
28522         this.el.hide();
28523         
28524         this.fireEvent('hide', this);
28525     },
28526     
28527     update : function()
28528     {
28529 //        var e = this.el.dom.firstChild;
28530 //        
28531 //        if(this.closable){
28532 //            e = e.nextSibling;
28533 //        }
28534 //        
28535 //        e.data = this.html || '';
28536
28537         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28538     }
28539    
28540 });
28541
28542  
28543
28544      /*
28545  * - LGPL
28546  *
28547  * Graph
28548  * 
28549  */
28550
28551
28552 /**
28553  * @class Roo.bootstrap.Graph
28554  * @extends Roo.bootstrap.Component
28555  * Bootstrap Graph class
28556 > Prameters
28557  -sm {number} sm 4
28558  -md {number} md 5
28559  @cfg {String} graphtype  bar | vbar | pie
28560  @cfg {number} g_x coodinator | centre x (pie)
28561  @cfg {number} g_y coodinator | centre y (pie)
28562  @cfg {number} g_r radius (pie)
28563  @cfg {number} g_height height of the chart (respected by all elements in the set)
28564  @cfg {number} g_width width of the chart (respected by all elements in the set)
28565  @cfg {Object} title The title of the chart
28566     
28567  -{Array}  values
28568  -opts (object) options for the chart 
28569      o {
28570      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28571      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28572      o vgutter (number)
28573      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.
28574      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28575      o to
28576      o stretch (boolean)
28577      o }
28578  -opts (object) options for the pie
28579      o{
28580      o cut
28581      o startAngle (number)
28582      o endAngle (number)
28583      } 
28584  *
28585  * @constructor
28586  * Create a new Input
28587  * @param {Object} config The config object
28588  */
28589
28590 Roo.bootstrap.Graph = function(config){
28591     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28592     
28593     this.addEvents({
28594         // img events
28595         /**
28596          * @event click
28597          * The img click event for the img.
28598          * @param {Roo.EventObject} e
28599          */
28600         "click" : true
28601     });
28602 };
28603
28604 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28605     
28606     sm: 4,
28607     md: 5,
28608     graphtype: 'bar',
28609     g_height: 250,
28610     g_width: 400,
28611     g_x: 50,
28612     g_y: 50,
28613     g_r: 30,
28614     opts:{
28615         //g_colors: this.colors,
28616         g_type: 'soft',
28617         g_gutter: '20%'
28618
28619     },
28620     title : false,
28621
28622     getAutoCreate : function(){
28623         
28624         var cfg = {
28625             tag: 'div',
28626             html : null
28627         };
28628         
28629         
28630         return  cfg;
28631     },
28632
28633     onRender : function(ct,position){
28634         
28635         
28636         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28637         
28638         if (typeof(Raphael) == 'undefined') {
28639             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28640             return;
28641         }
28642         
28643         this.raphael = Raphael(this.el.dom);
28644         
28645                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28646                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28647                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28648                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28649                 /*
28650                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28651                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28652                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28653                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28654                 
28655                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28656                 r.barchart(330, 10, 300, 220, data1);
28657                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28658                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28659                 */
28660                 
28661                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28662                 // r.barchart(30, 30, 560, 250,  xdata, {
28663                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28664                 //     axis : "0 0 1 1",
28665                 //     axisxlabels :  xdata
28666                 //     //yvalues : cols,
28667                    
28668                 // });
28669 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28670 //        
28671 //        this.load(null,xdata,{
28672 //                axis : "0 0 1 1",
28673 //                axisxlabels :  xdata
28674 //                });
28675
28676     },
28677
28678     load : function(graphtype,xdata,opts)
28679     {
28680         this.raphael.clear();
28681         if(!graphtype) {
28682             graphtype = this.graphtype;
28683         }
28684         if(!opts){
28685             opts = this.opts;
28686         }
28687         var r = this.raphael,
28688             fin = function () {
28689                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28690             },
28691             fout = function () {
28692                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28693             },
28694             pfin = function() {
28695                 this.sector.stop();
28696                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28697
28698                 if (this.label) {
28699                     this.label[0].stop();
28700                     this.label[0].attr({ r: 7.5 });
28701                     this.label[1].attr({ "font-weight": 800 });
28702                 }
28703             },
28704             pfout = function() {
28705                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28706
28707                 if (this.label) {
28708                     this.label[0].animate({ r: 5 }, 500, "bounce");
28709                     this.label[1].attr({ "font-weight": 400 });
28710                 }
28711             };
28712
28713         switch(graphtype){
28714             case 'bar':
28715                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28716                 break;
28717             case 'hbar':
28718                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28719                 break;
28720             case 'pie':
28721 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28722 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28723 //            
28724                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28725                 
28726                 break;
28727
28728         }
28729         
28730         if(this.title){
28731             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28732         }
28733         
28734     },
28735     
28736     setTitle: function(o)
28737     {
28738         this.title = o;
28739     },
28740     
28741     initEvents: function() {
28742         
28743         if(!this.href){
28744             this.el.on('click', this.onClick, this);
28745         }
28746     },
28747     
28748     onClick : function(e)
28749     {
28750         Roo.log('img onclick');
28751         this.fireEvent('click', this, e);
28752     }
28753    
28754 });
28755
28756  
28757 /*
28758  * - LGPL
28759  *
28760  * numberBox
28761  * 
28762  */
28763 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28764
28765 /**
28766  * @class Roo.bootstrap.dash.NumberBox
28767  * @extends Roo.bootstrap.Component
28768  * Bootstrap NumberBox class
28769  * @cfg {String} headline Box headline
28770  * @cfg {String} content Box content
28771  * @cfg {String} icon Box icon
28772  * @cfg {String} footer Footer text
28773  * @cfg {String} fhref Footer href
28774  * 
28775  * @constructor
28776  * Create a new NumberBox
28777  * @param {Object} config The config object
28778  */
28779
28780
28781 Roo.bootstrap.dash.NumberBox = function(config){
28782     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28783     
28784 };
28785
28786 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28787     
28788     headline : '',
28789     content : '',
28790     icon : '',
28791     footer : '',
28792     fhref : '',
28793     ficon : '',
28794     
28795     getAutoCreate : function(){
28796         
28797         var cfg = {
28798             tag : 'div',
28799             cls : 'small-box ',
28800             cn : [
28801                 {
28802                     tag : 'div',
28803                     cls : 'inner',
28804                     cn :[
28805                         {
28806                             tag : 'h3',
28807                             cls : 'roo-headline',
28808                             html : this.headline
28809                         },
28810                         {
28811                             tag : 'p',
28812                             cls : 'roo-content',
28813                             html : this.content
28814                         }
28815                     ]
28816                 }
28817             ]
28818         };
28819         
28820         if(this.icon){
28821             cfg.cn.push({
28822                 tag : 'div',
28823                 cls : 'icon',
28824                 cn :[
28825                     {
28826                         tag : 'i',
28827                         cls : 'ion ' + this.icon
28828                     }
28829                 ]
28830             });
28831         }
28832         
28833         if(this.footer){
28834             var footer = {
28835                 tag : 'a',
28836                 cls : 'small-box-footer',
28837                 href : this.fhref || '#',
28838                 html : this.footer
28839             };
28840             
28841             cfg.cn.push(footer);
28842             
28843         }
28844         
28845         return  cfg;
28846     },
28847
28848     onRender : function(ct,position){
28849         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28850
28851
28852        
28853                 
28854     },
28855
28856     setHeadline: function (value)
28857     {
28858         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28859     },
28860     
28861     setFooter: function (value, href)
28862     {
28863         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28864         
28865         if(href){
28866             this.el.select('a.small-box-footer',true).first().attr('href', href);
28867         }
28868         
28869     },
28870
28871     setContent: function (value)
28872     {
28873         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28874     },
28875
28876     initEvents: function() 
28877     {   
28878         
28879     }
28880     
28881 });
28882
28883  
28884 /*
28885  * - LGPL
28886  *
28887  * TabBox
28888  * 
28889  */
28890 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28891
28892 /**
28893  * @class Roo.bootstrap.dash.TabBox
28894  * @extends Roo.bootstrap.Component
28895  * Bootstrap TabBox class
28896  * @cfg {String} title Title of the TabBox
28897  * @cfg {String} icon Icon of the TabBox
28898  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28899  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28900  * 
28901  * @constructor
28902  * Create a new TabBox
28903  * @param {Object} config The config object
28904  */
28905
28906
28907 Roo.bootstrap.dash.TabBox = function(config){
28908     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28909     this.addEvents({
28910         // raw events
28911         /**
28912          * @event addpane
28913          * When a pane is added
28914          * @param {Roo.bootstrap.dash.TabPane} pane
28915          */
28916         "addpane" : true,
28917         /**
28918          * @event activatepane
28919          * When a pane is activated
28920          * @param {Roo.bootstrap.dash.TabPane} pane
28921          */
28922         "activatepane" : true
28923         
28924          
28925     });
28926     
28927     this.panes = [];
28928 };
28929
28930 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28931
28932     title : '',
28933     icon : false,
28934     showtabs : true,
28935     tabScrollable : false,
28936     
28937     getChildContainer : function()
28938     {
28939         return this.el.select('.tab-content', true).first();
28940     },
28941     
28942     getAutoCreate : function(){
28943         
28944         var header = {
28945             tag: 'li',
28946             cls: 'pull-left header',
28947             html: this.title,
28948             cn : []
28949         };
28950         
28951         if(this.icon){
28952             header.cn.push({
28953                 tag: 'i',
28954                 cls: 'fa ' + this.icon
28955             });
28956         }
28957         
28958         var h = {
28959             tag: 'ul',
28960             cls: 'nav nav-tabs pull-right',
28961             cn: [
28962                 header
28963             ]
28964         };
28965         
28966         if(this.tabScrollable){
28967             h = {
28968                 tag: 'div',
28969                 cls: 'tab-header',
28970                 cn: [
28971                     {
28972                         tag: 'ul',
28973                         cls: 'nav nav-tabs pull-right',
28974                         cn: [
28975                             header
28976                         ]
28977                     }
28978                 ]
28979             };
28980         }
28981         
28982         var cfg = {
28983             tag: 'div',
28984             cls: 'nav-tabs-custom',
28985             cn: [
28986                 h,
28987                 {
28988                     tag: 'div',
28989                     cls: 'tab-content no-padding',
28990                     cn: []
28991                 }
28992             ]
28993         };
28994
28995         return  cfg;
28996     },
28997     initEvents : function()
28998     {
28999         //Roo.log('add add pane handler');
29000         this.on('addpane', this.onAddPane, this);
29001     },
29002      /**
29003      * Updates the box title
29004      * @param {String} html to set the title to.
29005      */
29006     setTitle : function(value)
29007     {
29008         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29009     },
29010     onAddPane : function(pane)
29011     {
29012         this.panes.push(pane);
29013         //Roo.log('addpane');
29014         //Roo.log(pane);
29015         // tabs are rendere left to right..
29016         if(!this.showtabs){
29017             return;
29018         }
29019         
29020         var ctr = this.el.select('.nav-tabs', true).first();
29021          
29022          
29023         var existing = ctr.select('.nav-tab',true);
29024         var qty = existing.getCount();;
29025         
29026         
29027         var tab = ctr.createChild({
29028             tag : 'li',
29029             cls : 'nav-tab' + (qty ? '' : ' active'),
29030             cn : [
29031                 {
29032                     tag : 'a',
29033                     href:'#',
29034                     html : pane.title
29035                 }
29036             ]
29037         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29038         pane.tab = tab;
29039         
29040         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29041         if (!qty) {
29042             pane.el.addClass('active');
29043         }
29044         
29045                 
29046     },
29047     onTabClick : function(ev,un,ob,pane)
29048     {
29049         //Roo.log('tab - prev default');
29050         ev.preventDefault();
29051         
29052         
29053         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29054         pane.tab.addClass('active');
29055         //Roo.log(pane.title);
29056         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29057         // technically we should have a deactivate event.. but maybe add later.
29058         // and it should not de-activate the selected tab...
29059         this.fireEvent('activatepane', pane);
29060         pane.el.addClass('active');
29061         pane.fireEvent('activate');
29062         
29063         
29064     },
29065     
29066     getActivePane : function()
29067     {
29068         var r = false;
29069         Roo.each(this.panes, function(p) {
29070             if(p.el.hasClass('active')){
29071                 r = p;
29072                 return false;
29073             }
29074             
29075             return;
29076         });
29077         
29078         return r;
29079     }
29080     
29081     
29082 });
29083
29084  
29085 /*
29086  * - LGPL
29087  *
29088  * Tab pane
29089  * 
29090  */
29091 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29092 /**
29093  * @class Roo.bootstrap.TabPane
29094  * @extends Roo.bootstrap.Component
29095  * Bootstrap TabPane class
29096  * @cfg {Boolean} active (false | true) Default false
29097  * @cfg {String} title title of panel
29098
29099  * 
29100  * @constructor
29101  * Create a new TabPane
29102  * @param {Object} config The config object
29103  */
29104
29105 Roo.bootstrap.dash.TabPane = function(config){
29106     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29107     
29108     this.addEvents({
29109         // raw events
29110         /**
29111          * @event activate
29112          * When a pane is activated
29113          * @param {Roo.bootstrap.dash.TabPane} pane
29114          */
29115         "activate" : true
29116          
29117     });
29118 };
29119
29120 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29121     
29122     active : false,
29123     title : '',
29124     
29125     // the tabBox that this is attached to.
29126     tab : false,
29127      
29128     getAutoCreate : function() 
29129     {
29130         var cfg = {
29131             tag: 'div',
29132             cls: 'tab-pane'
29133         };
29134         
29135         if(this.active){
29136             cfg.cls += ' active';
29137         }
29138         
29139         return cfg;
29140     },
29141     initEvents  : function()
29142     {
29143         //Roo.log('trigger add pane handler');
29144         this.parent().fireEvent('addpane', this)
29145     },
29146     
29147      /**
29148      * Updates the tab title 
29149      * @param {String} html to set the title to.
29150      */
29151     setTitle: function(str)
29152     {
29153         if (!this.tab) {
29154             return;
29155         }
29156         this.title = str;
29157         this.tab.select('a', true).first().dom.innerHTML = str;
29158         
29159     }
29160     
29161     
29162     
29163 });
29164
29165  
29166
29167
29168  /*
29169  * - LGPL
29170  *
29171  * menu
29172  * 
29173  */
29174 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29175
29176 /**
29177  * @class Roo.bootstrap.menu.Menu
29178  * @extends Roo.bootstrap.Component
29179  * Bootstrap Menu class - container for Menu
29180  * @cfg {String} html Text of the menu
29181  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29182  * @cfg {String} icon Font awesome icon
29183  * @cfg {String} pos Menu align to (top | bottom) default bottom
29184  * 
29185  * 
29186  * @constructor
29187  * Create a new Menu
29188  * @param {Object} config The config object
29189  */
29190
29191
29192 Roo.bootstrap.menu.Menu = function(config){
29193     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29194     
29195     this.addEvents({
29196         /**
29197          * @event beforeshow
29198          * Fires before this menu is displayed
29199          * @param {Roo.bootstrap.menu.Menu} this
29200          */
29201         beforeshow : true,
29202         /**
29203          * @event beforehide
29204          * Fires before this menu is hidden
29205          * @param {Roo.bootstrap.menu.Menu} this
29206          */
29207         beforehide : true,
29208         /**
29209          * @event show
29210          * Fires after this menu is displayed
29211          * @param {Roo.bootstrap.menu.Menu} this
29212          */
29213         show : true,
29214         /**
29215          * @event hide
29216          * Fires after this menu is hidden
29217          * @param {Roo.bootstrap.menu.Menu} this
29218          */
29219         hide : true,
29220         /**
29221          * @event click
29222          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29223          * @param {Roo.bootstrap.menu.Menu} this
29224          * @param {Roo.EventObject} e
29225          */
29226         click : true
29227     });
29228     
29229 };
29230
29231 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29232     
29233     submenu : false,
29234     html : '',
29235     weight : 'default',
29236     icon : false,
29237     pos : 'bottom',
29238     
29239     
29240     getChildContainer : function() {
29241         if(this.isSubMenu){
29242             return this.el;
29243         }
29244         
29245         return this.el.select('ul.dropdown-menu', true).first();  
29246     },
29247     
29248     getAutoCreate : function()
29249     {
29250         var text = [
29251             {
29252                 tag : 'span',
29253                 cls : 'roo-menu-text',
29254                 html : this.html
29255             }
29256         ];
29257         
29258         if(this.icon){
29259             text.unshift({
29260                 tag : 'i',
29261                 cls : 'fa ' + this.icon
29262             })
29263         }
29264         
29265         
29266         var cfg = {
29267             tag : 'div',
29268             cls : 'btn-group',
29269             cn : [
29270                 {
29271                     tag : 'button',
29272                     cls : 'dropdown-button btn btn-' + this.weight,
29273                     cn : text
29274                 },
29275                 {
29276                     tag : 'button',
29277                     cls : 'dropdown-toggle btn btn-' + this.weight,
29278                     cn : [
29279                         {
29280                             tag : 'span',
29281                             cls : 'caret'
29282                         }
29283                     ]
29284                 },
29285                 {
29286                     tag : 'ul',
29287                     cls : 'dropdown-menu'
29288                 }
29289             ]
29290             
29291         };
29292         
29293         if(this.pos == 'top'){
29294             cfg.cls += ' dropup';
29295         }
29296         
29297         if(this.isSubMenu){
29298             cfg = {
29299                 tag : 'ul',
29300                 cls : 'dropdown-menu'
29301             }
29302         }
29303         
29304         return cfg;
29305     },
29306     
29307     onRender : function(ct, position)
29308     {
29309         this.isSubMenu = ct.hasClass('dropdown-submenu');
29310         
29311         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29312     },
29313     
29314     initEvents : function() 
29315     {
29316         if(this.isSubMenu){
29317             return;
29318         }
29319         
29320         this.hidden = true;
29321         
29322         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29323         this.triggerEl.on('click', this.onTriggerPress, this);
29324         
29325         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29326         this.buttonEl.on('click', this.onClick, this);
29327         
29328     },
29329     
29330     list : function()
29331     {
29332         if(this.isSubMenu){
29333             return this.el;
29334         }
29335         
29336         return this.el.select('ul.dropdown-menu', true).first();
29337     },
29338     
29339     onClick : function(e)
29340     {
29341         this.fireEvent("click", this, e);
29342     },
29343     
29344     onTriggerPress  : function(e)
29345     {   
29346         if (this.isVisible()) {
29347             this.hide();
29348         } else {
29349             this.show();
29350         }
29351     },
29352     
29353     isVisible : function(){
29354         return !this.hidden;
29355     },
29356     
29357     show : function()
29358     {
29359         this.fireEvent("beforeshow", this);
29360         
29361         this.hidden = false;
29362         this.el.addClass('open');
29363         
29364         Roo.get(document).on("mouseup", this.onMouseUp, this);
29365         
29366         this.fireEvent("show", this);
29367         
29368         
29369     },
29370     
29371     hide : function()
29372     {
29373         this.fireEvent("beforehide", this);
29374         
29375         this.hidden = true;
29376         this.el.removeClass('open');
29377         
29378         Roo.get(document).un("mouseup", this.onMouseUp);
29379         
29380         this.fireEvent("hide", this);
29381     },
29382     
29383     onMouseUp : function()
29384     {
29385         this.hide();
29386     }
29387     
29388 });
29389
29390  
29391  /*
29392  * - LGPL
29393  *
29394  * menu item
29395  * 
29396  */
29397 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29398
29399 /**
29400  * @class Roo.bootstrap.menu.Item
29401  * @extends Roo.bootstrap.Component
29402  * Bootstrap MenuItem class
29403  * @cfg {Boolean} submenu (true | false) default false
29404  * @cfg {String} html text of the item
29405  * @cfg {String} href the link
29406  * @cfg {Boolean} disable (true | false) default false
29407  * @cfg {Boolean} preventDefault (true | false) default true
29408  * @cfg {String} icon Font awesome icon
29409  * @cfg {String} pos Submenu align to (left | right) default right 
29410  * 
29411  * 
29412  * @constructor
29413  * Create a new Item
29414  * @param {Object} config The config object
29415  */
29416
29417
29418 Roo.bootstrap.menu.Item = function(config){
29419     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29420     this.addEvents({
29421         /**
29422          * @event mouseover
29423          * Fires when the mouse is hovering over this menu
29424          * @param {Roo.bootstrap.menu.Item} this
29425          * @param {Roo.EventObject} e
29426          */
29427         mouseover : true,
29428         /**
29429          * @event mouseout
29430          * Fires when the mouse exits this menu
29431          * @param {Roo.bootstrap.menu.Item} this
29432          * @param {Roo.EventObject} e
29433          */
29434         mouseout : true,
29435         // raw events
29436         /**
29437          * @event click
29438          * The raw click event for the entire grid.
29439          * @param {Roo.EventObject} e
29440          */
29441         click : true
29442     });
29443 };
29444
29445 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29446     
29447     submenu : false,
29448     href : '',
29449     html : '',
29450     preventDefault: true,
29451     disable : false,
29452     icon : false,
29453     pos : 'right',
29454     
29455     getAutoCreate : function()
29456     {
29457         var text = [
29458             {
29459                 tag : 'span',
29460                 cls : 'roo-menu-item-text',
29461                 html : this.html
29462             }
29463         ];
29464         
29465         if(this.icon){
29466             text.unshift({
29467                 tag : 'i',
29468                 cls : 'fa ' + this.icon
29469             })
29470         }
29471         
29472         var cfg = {
29473             tag : 'li',
29474             cn : [
29475                 {
29476                     tag : 'a',
29477                     href : this.href || '#',
29478                     cn : text
29479                 }
29480             ]
29481         };
29482         
29483         if(this.disable){
29484             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29485         }
29486         
29487         if(this.submenu){
29488             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29489             
29490             if(this.pos == 'left'){
29491                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29492             }
29493         }
29494         
29495         return cfg;
29496     },
29497     
29498     initEvents : function() 
29499     {
29500         this.el.on('mouseover', this.onMouseOver, this);
29501         this.el.on('mouseout', this.onMouseOut, this);
29502         
29503         this.el.select('a', true).first().on('click', this.onClick, this);
29504         
29505     },
29506     
29507     onClick : function(e)
29508     {
29509         if(this.preventDefault){
29510             e.preventDefault();
29511         }
29512         
29513         this.fireEvent("click", this, e);
29514     },
29515     
29516     onMouseOver : function(e)
29517     {
29518         if(this.submenu && this.pos == 'left'){
29519             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29520         }
29521         
29522         this.fireEvent("mouseover", this, e);
29523     },
29524     
29525     onMouseOut : function(e)
29526     {
29527         this.fireEvent("mouseout", this, e);
29528     }
29529 });
29530
29531  
29532
29533  /*
29534  * - LGPL
29535  *
29536  * menu separator
29537  * 
29538  */
29539 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29540
29541 /**
29542  * @class Roo.bootstrap.menu.Separator
29543  * @extends Roo.bootstrap.Component
29544  * Bootstrap Separator class
29545  * 
29546  * @constructor
29547  * Create a new Separator
29548  * @param {Object} config The config object
29549  */
29550
29551
29552 Roo.bootstrap.menu.Separator = function(config){
29553     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29554 };
29555
29556 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29557     
29558     getAutoCreate : function(){
29559         var cfg = {
29560             tag : 'li',
29561             cls: 'dropdown-divider divider'
29562         };
29563         
29564         return cfg;
29565     }
29566    
29567 });
29568
29569  
29570
29571  /*
29572  * - LGPL
29573  *
29574  * Tooltip
29575  * 
29576  */
29577
29578 /**
29579  * @class Roo.bootstrap.Tooltip
29580  * Bootstrap Tooltip class
29581  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29582  * to determine which dom element triggers the tooltip.
29583  * 
29584  * It needs to add support for additional attributes like tooltip-position
29585  * 
29586  * @constructor
29587  * Create a new Toolti
29588  * @param {Object} config The config object
29589  */
29590
29591 Roo.bootstrap.Tooltip = function(config){
29592     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29593     
29594     this.alignment = Roo.bootstrap.Tooltip.alignment;
29595     
29596     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29597         this.alignment = config.alignment;
29598     }
29599     
29600 };
29601
29602 Roo.apply(Roo.bootstrap.Tooltip, {
29603     /**
29604      * @function init initialize tooltip monitoring.
29605      * @static
29606      */
29607     currentEl : false,
29608     currentTip : false,
29609     currentRegion : false,
29610     
29611     //  init : delay?
29612     
29613     init : function()
29614     {
29615         Roo.get(document).on('mouseover', this.enter ,this);
29616         Roo.get(document).on('mouseout', this.leave, this);
29617          
29618         
29619         this.currentTip = new Roo.bootstrap.Tooltip();
29620     },
29621     
29622     enter : function(ev)
29623     {
29624         var dom = ev.getTarget();
29625         
29626         //Roo.log(['enter',dom]);
29627         var el = Roo.fly(dom);
29628         if (this.currentEl) {
29629             //Roo.log(dom);
29630             //Roo.log(this.currentEl);
29631             //Roo.log(this.currentEl.contains(dom));
29632             if (this.currentEl == el) {
29633                 return;
29634             }
29635             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29636                 return;
29637             }
29638
29639         }
29640         
29641         if (this.currentTip.el) {
29642             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29643         }    
29644         //Roo.log(ev);
29645         
29646         if(!el || el.dom == document){
29647             return;
29648         }
29649         
29650         var bindEl = el; 
29651         var pel = false;
29652         if (!el.attr('tooltip')) {
29653             pel = el.findParent("[tooltip]");
29654             if (pel) {
29655                 bindEl = Roo.get(pel);
29656             }
29657         }
29658         
29659        
29660         
29661         // you can not look for children, as if el is the body.. then everythign is the child..
29662         if (!pel && !el.attr('tooltip')) { //
29663             if (!el.select("[tooltip]").elements.length) {
29664                 return;
29665             }
29666             // is the mouse over this child...?
29667             bindEl = el.select("[tooltip]").first();
29668             var xy = ev.getXY();
29669             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29670                 //Roo.log("not in region.");
29671                 return;
29672             }
29673             //Roo.log("child element over..");
29674             
29675         }
29676         this.currentEl = el;
29677         this.currentTip.bind(bindEl);
29678         this.currentRegion = Roo.lib.Region.getRegion(dom);
29679         this.currentTip.enter();
29680         
29681     },
29682     leave : function(ev)
29683     {
29684         var dom = ev.getTarget();
29685         //Roo.log(['leave',dom]);
29686         if (!this.currentEl) {
29687             return;
29688         }
29689         
29690         
29691         if (dom != this.currentEl.dom) {
29692             return;
29693         }
29694         var xy = ev.getXY();
29695         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29696             return;
29697         }
29698         // only activate leave if mouse cursor is outside... bounding box..
29699         
29700         
29701         
29702         
29703         if (this.currentTip) {
29704             this.currentTip.leave();
29705         }
29706         //Roo.log('clear currentEl');
29707         this.currentEl = false;
29708         
29709         
29710     },
29711     alignment : {
29712         'left' : ['r-l', [-2,0], 'right'],
29713         'right' : ['l-r', [2,0], 'left'],
29714         'bottom' : ['t-b', [0,2], 'top'],
29715         'top' : [ 'b-t', [0,-2], 'bottom']
29716     }
29717     
29718 });
29719
29720
29721 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29722     
29723     
29724     bindEl : false,
29725     
29726     delay : null, // can be { show : 300 , hide: 500}
29727     
29728     timeout : null,
29729     
29730     hoverState : null, //???
29731     
29732     placement : 'bottom', 
29733     
29734     alignment : false,
29735     
29736     getAutoCreate : function(){
29737     
29738         var cfg = {
29739            cls : 'tooltip',   
29740            role : 'tooltip',
29741            cn : [
29742                 {
29743                     cls : 'tooltip-arrow arrow'
29744                 },
29745                 {
29746                     cls : 'tooltip-inner'
29747                 }
29748            ]
29749         };
29750         
29751         return cfg;
29752     },
29753     bind : function(el)
29754     {
29755         this.bindEl = el;
29756     },
29757     
29758     initEvents : function()
29759     {
29760         this.arrowEl = this.el.select('.arrow', true).first();
29761         this.innerEl = this.el.select('.tooltip-inner', true).first();
29762     },
29763     
29764     enter : function () {
29765        
29766         if (this.timeout != null) {
29767             clearTimeout(this.timeout);
29768         }
29769         
29770         this.hoverState = 'in';
29771          //Roo.log("enter - show");
29772         if (!this.delay || !this.delay.show) {
29773             this.show();
29774             return;
29775         }
29776         var _t = this;
29777         this.timeout = setTimeout(function () {
29778             if (_t.hoverState == 'in') {
29779                 _t.show();
29780             }
29781         }, this.delay.show);
29782     },
29783     leave : function()
29784     {
29785         clearTimeout(this.timeout);
29786     
29787         this.hoverState = 'out';
29788          if (!this.delay || !this.delay.hide) {
29789             this.hide();
29790             return;
29791         }
29792        
29793         var _t = this;
29794         this.timeout = setTimeout(function () {
29795             //Roo.log("leave - timeout");
29796             
29797             if (_t.hoverState == 'out') {
29798                 _t.hide();
29799                 Roo.bootstrap.Tooltip.currentEl = false;
29800             }
29801         }, delay);
29802     },
29803     
29804     show : function (msg)
29805     {
29806         if (!this.el) {
29807             this.render(document.body);
29808         }
29809         // set content.
29810         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29811         
29812         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29813         
29814         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29815         
29816         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29817                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29818         
29819         var placement = typeof this.placement == 'function' ?
29820             this.placement.call(this, this.el, on_el) :
29821             this.placement;
29822             
29823         var autoToken = /\s?auto?\s?/i;
29824         var autoPlace = autoToken.test(placement);
29825         if (autoPlace) {
29826             placement = placement.replace(autoToken, '') || 'top';
29827         }
29828         
29829         //this.el.detach()
29830         //this.el.setXY([0,0]);
29831         this.el.show();
29832         //this.el.dom.style.display='block';
29833         
29834         //this.el.appendTo(on_el);
29835         
29836         var p = this.getPosition();
29837         var box = this.el.getBox();
29838         
29839         if (autoPlace) {
29840             // fixme..
29841         }
29842         
29843         var align = this.alignment[placement];
29844         
29845         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29846         
29847         if(placement == 'top' || placement == 'bottom'){
29848             if(xy[0] < 0){
29849                 placement = 'right';
29850             }
29851             
29852             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29853                 placement = 'left';
29854             }
29855             
29856             var scroll = Roo.select('body', true).first().getScroll();
29857             
29858             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29859                 placement = 'top';
29860             }
29861             
29862             align = this.alignment[placement];
29863             
29864             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29865             
29866         }
29867         
29868         var elems = document.getElementsByTagName('div');
29869         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29870         for (var i = 0; i < elems.length; i++) {
29871           var zindex = Number.parseInt(
29872                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29873                 10
29874           );
29875           if (zindex > highest) {
29876             highest = zindex;
29877           }
29878         }
29879         
29880         
29881         
29882         this.el.dom.style.zIndex = highest;
29883         
29884         this.el.alignTo(this.bindEl, align[0],align[1]);
29885         //var arrow = this.el.select('.arrow',true).first();
29886         //arrow.set(align[2], 
29887         
29888         this.el.addClass(placement);
29889         this.el.addClass("bs-tooltip-"+ placement);
29890         
29891         this.el.addClass('in fade show');
29892         
29893         this.hoverState = null;
29894         
29895         if (this.el.hasClass('fade')) {
29896             // fade it?
29897         }
29898         
29899         
29900         
29901         
29902         
29903     },
29904     hide : function()
29905     {
29906          
29907         if (!this.el) {
29908             return;
29909         }
29910         //this.el.setXY([0,0]);
29911         this.el.removeClass(['show', 'in']);
29912         //this.el.hide();
29913         
29914     }
29915     
29916 });
29917  
29918
29919  /*
29920  * - LGPL
29921  *
29922  * Location Picker
29923  * 
29924  */
29925
29926 /**
29927  * @class Roo.bootstrap.LocationPicker
29928  * @extends Roo.bootstrap.Component
29929  * Bootstrap LocationPicker class
29930  * @cfg {Number} latitude Position when init default 0
29931  * @cfg {Number} longitude Position when init default 0
29932  * @cfg {Number} zoom default 15
29933  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29934  * @cfg {Boolean} mapTypeControl default false
29935  * @cfg {Boolean} disableDoubleClickZoom default false
29936  * @cfg {Boolean} scrollwheel default true
29937  * @cfg {Boolean} streetViewControl default false
29938  * @cfg {Number} radius default 0
29939  * @cfg {String} locationName
29940  * @cfg {Boolean} draggable default true
29941  * @cfg {Boolean} enableAutocomplete default false
29942  * @cfg {Boolean} enableReverseGeocode default true
29943  * @cfg {String} markerTitle
29944  * 
29945  * @constructor
29946  * Create a new LocationPicker
29947  * @param {Object} config The config object
29948  */
29949
29950
29951 Roo.bootstrap.LocationPicker = function(config){
29952     
29953     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29954     
29955     this.addEvents({
29956         /**
29957          * @event initial
29958          * Fires when the picker initialized.
29959          * @param {Roo.bootstrap.LocationPicker} this
29960          * @param {Google Location} location
29961          */
29962         initial : true,
29963         /**
29964          * @event positionchanged
29965          * Fires when the picker position changed.
29966          * @param {Roo.bootstrap.LocationPicker} this
29967          * @param {Google Location} location
29968          */
29969         positionchanged : true,
29970         /**
29971          * @event resize
29972          * Fires when the map resize.
29973          * @param {Roo.bootstrap.LocationPicker} this
29974          */
29975         resize : true,
29976         /**
29977          * @event show
29978          * Fires when the map show.
29979          * @param {Roo.bootstrap.LocationPicker} this
29980          */
29981         show : true,
29982         /**
29983          * @event hide
29984          * Fires when the map hide.
29985          * @param {Roo.bootstrap.LocationPicker} this
29986          */
29987         hide : true,
29988         /**
29989          * @event mapClick
29990          * Fires when click the map.
29991          * @param {Roo.bootstrap.LocationPicker} this
29992          * @param {Map event} e
29993          */
29994         mapClick : true,
29995         /**
29996          * @event mapRightClick
29997          * Fires when right click the map.
29998          * @param {Roo.bootstrap.LocationPicker} this
29999          * @param {Map event} e
30000          */
30001         mapRightClick : true,
30002         /**
30003          * @event markerClick
30004          * Fires when click the marker.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          * @param {Map event} e
30007          */
30008         markerClick : true,
30009         /**
30010          * @event markerRightClick
30011          * Fires when right click the marker.
30012          * @param {Roo.bootstrap.LocationPicker} this
30013          * @param {Map event} e
30014          */
30015         markerRightClick : true,
30016         /**
30017          * @event OverlayViewDraw
30018          * Fires when OverlayView Draw
30019          * @param {Roo.bootstrap.LocationPicker} this
30020          */
30021         OverlayViewDraw : true,
30022         /**
30023          * @event OverlayViewOnAdd
30024          * Fires when OverlayView Draw
30025          * @param {Roo.bootstrap.LocationPicker} this
30026          */
30027         OverlayViewOnAdd : true,
30028         /**
30029          * @event OverlayViewOnRemove
30030          * Fires when OverlayView Draw
30031          * @param {Roo.bootstrap.LocationPicker} this
30032          */
30033         OverlayViewOnRemove : true,
30034         /**
30035          * @event OverlayViewShow
30036          * Fires when OverlayView Draw
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          * @param {Pixel} cpx
30039          */
30040         OverlayViewShow : true,
30041         /**
30042          * @event OverlayViewHide
30043          * Fires when OverlayView Draw
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          */
30046         OverlayViewHide : true,
30047         /**
30048          * @event loadexception
30049          * Fires when load google lib failed.
30050          * @param {Roo.bootstrap.LocationPicker} this
30051          */
30052         loadexception : true
30053     });
30054         
30055 };
30056
30057 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30058     
30059     gMapContext: false,
30060     
30061     latitude: 0,
30062     longitude: 0,
30063     zoom: 15,
30064     mapTypeId: false,
30065     mapTypeControl: false,
30066     disableDoubleClickZoom: false,
30067     scrollwheel: true,
30068     streetViewControl: false,
30069     radius: 0,
30070     locationName: '',
30071     draggable: true,
30072     enableAutocomplete: false,
30073     enableReverseGeocode: true,
30074     markerTitle: '',
30075     
30076     getAutoCreate: function()
30077     {
30078
30079         var cfg = {
30080             tag: 'div',
30081             cls: 'roo-location-picker'
30082         };
30083         
30084         return cfg
30085     },
30086     
30087     initEvents: function(ct, position)
30088     {       
30089         if(!this.el.getWidth() || this.isApplied()){
30090             return;
30091         }
30092         
30093         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30094         
30095         this.initial();
30096     },
30097     
30098     initial: function()
30099     {
30100         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30101             this.fireEvent('loadexception', this);
30102             return;
30103         }
30104         
30105         if(!this.mapTypeId){
30106             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30107         }
30108         
30109         this.gMapContext = this.GMapContext();
30110         
30111         this.initOverlayView();
30112         
30113         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30114         
30115         var _this = this;
30116                 
30117         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30118             _this.setPosition(_this.gMapContext.marker.position);
30119         });
30120         
30121         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30122             _this.fireEvent('mapClick', this, event);
30123             
30124         });
30125
30126         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30127             _this.fireEvent('mapRightClick', this, event);
30128             
30129         });
30130         
30131         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30132             _this.fireEvent('markerClick', this, event);
30133             
30134         });
30135
30136         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30137             _this.fireEvent('markerRightClick', this, event);
30138             
30139         });
30140         
30141         this.setPosition(this.gMapContext.location);
30142         
30143         this.fireEvent('initial', this, this.gMapContext.location);
30144     },
30145     
30146     initOverlayView: function()
30147     {
30148         var _this = this;
30149         
30150         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30151             
30152             draw: function()
30153             {
30154                 _this.fireEvent('OverlayViewDraw', _this);
30155             },
30156             
30157             onAdd: function()
30158             {
30159                 _this.fireEvent('OverlayViewOnAdd', _this);
30160             },
30161             
30162             onRemove: function()
30163             {
30164                 _this.fireEvent('OverlayViewOnRemove', _this);
30165             },
30166             
30167             show: function(cpx)
30168             {
30169                 _this.fireEvent('OverlayViewShow', _this, cpx);
30170             },
30171             
30172             hide: function()
30173             {
30174                 _this.fireEvent('OverlayViewHide', _this);
30175             }
30176             
30177         });
30178     },
30179     
30180     fromLatLngToContainerPixel: function(event)
30181     {
30182         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30183     },
30184     
30185     isApplied: function() 
30186     {
30187         return this.getGmapContext() == false ? false : true;
30188     },
30189     
30190     getGmapContext: function() 
30191     {
30192         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30193     },
30194     
30195     GMapContext: function() 
30196     {
30197         var position = new google.maps.LatLng(this.latitude, this.longitude);
30198         
30199         var _map = new google.maps.Map(this.el.dom, {
30200             center: position,
30201             zoom: this.zoom,
30202             mapTypeId: this.mapTypeId,
30203             mapTypeControl: this.mapTypeControl,
30204             disableDoubleClickZoom: this.disableDoubleClickZoom,
30205             scrollwheel: this.scrollwheel,
30206             streetViewControl: this.streetViewControl,
30207             locationName: this.locationName,
30208             draggable: this.draggable,
30209             enableAutocomplete: this.enableAutocomplete,
30210             enableReverseGeocode: this.enableReverseGeocode
30211         });
30212         
30213         var _marker = new google.maps.Marker({
30214             position: position,
30215             map: _map,
30216             title: this.markerTitle,
30217             draggable: this.draggable
30218         });
30219         
30220         return {
30221             map: _map,
30222             marker: _marker,
30223             circle: null,
30224             location: position,
30225             radius: this.radius,
30226             locationName: this.locationName,
30227             addressComponents: {
30228                 formatted_address: null,
30229                 addressLine1: null,
30230                 addressLine2: null,
30231                 streetName: null,
30232                 streetNumber: null,
30233                 city: null,
30234                 district: null,
30235                 state: null,
30236                 stateOrProvince: null
30237             },
30238             settings: this,
30239             domContainer: this.el.dom,
30240             geodecoder: new google.maps.Geocoder()
30241         };
30242     },
30243     
30244     drawCircle: function(center, radius, options) 
30245     {
30246         if (this.gMapContext.circle != null) {
30247             this.gMapContext.circle.setMap(null);
30248         }
30249         if (radius > 0) {
30250             radius *= 1;
30251             options = Roo.apply({}, options, {
30252                 strokeColor: "#0000FF",
30253                 strokeOpacity: .35,
30254                 strokeWeight: 2,
30255                 fillColor: "#0000FF",
30256                 fillOpacity: .2
30257             });
30258             
30259             options.map = this.gMapContext.map;
30260             options.radius = radius;
30261             options.center = center;
30262             this.gMapContext.circle = new google.maps.Circle(options);
30263             return this.gMapContext.circle;
30264         }
30265         
30266         return null;
30267     },
30268     
30269     setPosition: function(location) 
30270     {
30271         this.gMapContext.location = location;
30272         this.gMapContext.marker.setPosition(location);
30273         this.gMapContext.map.panTo(location);
30274         this.drawCircle(location, this.gMapContext.radius, {});
30275         
30276         var _this = this;
30277         
30278         if (this.gMapContext.settings.enableReverseGeocode) {
30279             this.gMapContext.geodecoder.geocode({
30280                 latLng: this.gMapContext.location
30281             }, function(results, status) {
30282                 
30283                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30284                     _this.gMapContext.locationName = results[0].formatted_address;
30285                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30286                     
30287                     _this.fireEvent('positionchanged', this, location);
30288                 }
30289             });
30290             
30291             return;
30292         }
30293         
30294         this.fireEvent('positionchanged', this, location);
30295     },
30296     
30297     resize: function()
30298     {
30299         google.maps.event.trigger(this.gMapContext.map, "resize");
30300         
30301         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30302         
30303         this.fireEvent('resize', this);
30304     },
30305     
30306     setPositionByLatLng: function(latitude, longitude)
30307     {
30308         this.setPosition(new google.maps.LatLng(latitude, longitude));
30309     },
30310     
30311     getCurrentPosition: function() 
30312     {
30313         return {
30314             latitude: this.gMapContext.location.lat(),
30315             longitude: this.gMapContext.location.lng()
30316         };
30317     },
30318     
30319     getAddressName: function() 
30320     {
30321         return this.gMapContext.locationName;
30322     },
30323     
30324     getAddressComponents: function() 
30325     {
30326         return this.gMapContext.addressComponents;
30327     },
30328     
30329     address_component_from_google_geocode: function(address_components) 
30330     {
30331         var result = {};
30332         
30333         for (var i = 0; i < address_components.length; i++) {
30334             var component = address_components[i];
30335             if (component.types.indexOf("postal_code") >= 0) {
30336                 result.postalCode = component.short_name;
30337             } else if (component.types.indexOf("street_number") >= 0) {
30338                 result.streetNumber = component.short_name;
30339             } else if (component.types.indexOf("route") >= 0) {
30340                 result.streetName = component.short_name;
30341             } else if (component.types.indexOf("neighborhood") >= 0) {
30342                 result.city = component.short_name;
30343             } else if (component.types.indexOf("locality") >= 0) {
30344                 result.city = component.short_name;
30345             } else if (component.types.indexOf("sublocality") >= 0) {
30346                 result.district = component.short_name;
30347             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30348                 result.stateOrProvince = component.short_name;
30349             } else if (component.types.indexOf("country") >= 0) {
30350                 result.country = component.short_name;
30351             }
30352         }
30353         
30354         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30355         result.addressLine2 = "";
30356         return result;
30357     },
30358     
30359     setZoomLevel: function(zoom)
30360     {
30361         this.gMapContext.map.setZoom(zoom);
30362     },
30363     
30364     show: function()
30365     {
30366         if(!this.el){
30367             return;
30368         }
30369         
30370         this.el.show();
30371         
30372         this.resize();
30373         
30374         this.fireEvent('show', this);
30375     },
30376     
30377     hide: function()
30378     {
30379         if(!this.el){
30380             return;
30381         }
30382         
30383         this.el.hide();
30384         
30385         this.fireEvent('hide', this);
30386     }
30387     
30388 });
30389
30390 Roo.apply(Roo.bootstrap.LocationPicker, {
30391     
30392     OverlayView : function(map, options)
30393     {
30394         options = options || {};
30395         
30396         this.setMap(map);
30397     }
30398     
30399     
30400 });/**
30401  * @class Roo.bootstrap.Alert
30402  * @extends Roo.bootstrap.Component
30403  * Bootstrap Alert class - shows an alert area box
30404  * eg
30405  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30406   Enter a valid email address
30407 </div>
30408  * @licence LGPL
30409  * @cfg {String} title The title of alert
30410  * @cfg {String} html The content of alert
30411  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30412  * @cfg {String} fa font-awesomeicon
30413  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30414  * @cfg {Boolean} close true to show a x closer
30415  * 
30416  * 
30417  * @constructor
30418  * Create a new alert
30419  * @param {Object} config The config object
30420  */
30421
30422
30423 Roo.bootstrap.Alert = function(config){
30424     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30425     
30426 };
30427
30428 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30429     
30430     title: '',
30431     html: '',
30432     weight: false,
30433     fa: false,
30434     faicon: false, // BC
30435     close : false,
30436     
30437     
30438     getAutoCreate : function()
30439     {
30440         
30441         var cfg = {
30442             tag : 'div',
30443             cls : 'alert',
30444             cn : [
30445                 {
30446                     tag: 'button',
30447                     type :  "button",
30448                     cls: "close",
30449                     html : '×',
30450                     style : this.close ? '' : 'display:none'
30451                 },
30452                 {
30453                     tag : 'i',
30454                     cls : 'roo-alert-icon'
30455                     
30456                 },
30457                 {
30458                     tag : 'b',
30459                     cls : 'roo-alert-title',
30460                     html : this.title
30461                 },
30462                 {
30463                     tag : 'span',
30464                     cls : 'roo-alert-text',
30465                     html : this.html
30466                 }
30467             ]
30468         };
30469         
30470         if(this.faicon){
30471             cfg.cn[0].cls += ' fa ' + this.faicon;
30472         }
30473         if(this.fa){
30474             cfg.cn[0].cls += ' fa ' + this.fa;
30475         }
30476         
30477         if(this.weight){
30478             cfg.cls += ' alert-' + this.weight;
30479         }
30480         
30481         return cfg;
30482     },
30483     
30484     initEvents: function() 
30485     {
30486         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30487         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30488         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30489         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30490         if (this.seconds > 0) {
30491             this.hide.defer(this.seconds, this);
30492         }
30493     },
30494     /**
30495      * Set the Title Message HTML
30496      * @param {String} html
30497      */
30498     setTitle : function(str)
30499     {
30500         this.titleEl.dom.innerHTML = str;
30501     },
30502      
30503      /**
30504      * Set the Body Message HTML
30505      * @param {String} html
30506      */
30507     setHtml : function(str)
30508     {
30509         this.htmlEl.dom.innerHTML = str;
30510     },
30511     /**
30512      * Set the Weight of the alert
30513      * @param {String} (success|info|warning|danger) weight
30514      */
30515     
30516     setWeight : function(weight)
30517     {
30518         if(this.weight){
30519             this.el.removeClass('alert-' + this.weight);
30520         }
30521         
30522         this.weight = weight;
30523         
30524         this.el.addClass('alert-' + this.weight);
30525     },
30526       /**
30527      * Set the Icon of the alert
30528      * @param {String} see fontawsome names (name without the 'fa-' bit)
30529      */
30530     setIcon : function(icon)
30531     {
30532         if(this.faicon){
30533             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30534         }
30535         
30536         this.faicon = icon;
30537         
30538         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30539     },
30540     /**
30541      * Hide the Alert
30542      */
30543     hide: function() 
30544     {
30545         this.el.hide();   
30546     },
30547     /**
30548      * Show the Alert
30549      */
30550     show: function() 
30551     {  
30552         this.el.show();   
30553     }
30554     
30555 });
30556
30557  
30558 /*
30559 * Licence: LGPL
30560 */
30561
30562 /**
30563  * @class Roo.bootstrap.UploadCropbox
30564  * @extends Roo.bootstrap.Component
30565  * Bootstrap UploadCropbox class
30566  * @cfg {String} emptyText show when image has been loaded
30567  * @cfg {String} rotateNotify show when image too small to rotate
30568  * @cfg {Number} errorTimeout default 3000
30569  * @cfg {Number} minWidth default 300
30570  * @cfg {Number} minHeight default 300
30571  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30572  * @cfg {Boolean} isDocument (true|false) default false
30573  * @cfg {String} url action url
30574  * @cfg {String} paramName default 'imageUpload'
30575  * @cfg {String} method default POST
30576  * @cfg {Boolean} loadMask (true|false) default true
30577  * @cfg {Boolean} loadingText default 'Loading...'
30578  * 
30579  * @constructor
30580  * Create a new UploadCropbox
30581  * @param {Object} config The config object
30582  */
30583
30584 Roo.bootstrap.UploadCropbox = function(config){
30585     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30586     
30587     this.addEvents({
30588         /**
30589          * @event beforeselectfile
30590          * Fire before select file
30591          * @param {Roo.bootstrap.UploadCropbox} this
30592          */
30593         "beforeselectfile" : true,
30594         /**
30595          * @event initial
30596          * Fire after initEvent
30597          * @param {Roo.bootstrap.UploadCropbox} this
30598          */
30599         "initial" : true,
30600         /**
30601          * @event crop
30602          * Fire after initEvent
30603          * @param {Roo.bootstrap.UploadCropbox} this
30604          * @param {String} data
30605          */
30606         "crop" : true,
30607         /**
30608          * @event prepare
30609          * Fire when preparing the file data
30610          * @param {Roo.bootstrap.UploadCropbox} this
30611          * @param {Object} file
30612          */
30613         "prepare" : true,
30614         /**
30615          * @event exception
30616          * Fire when get exception
30617          * @param {Roo.bootstrap.UploadCropbox} this
30618          * @param {XMLHttpRequest} xhr
30619          */
30620         "exception" : true,
30621         /**
30622          * @event beforeloadcanvas
30623          * Fire before load the canvas
30624          * @param {Roo.bootstrap.UploadCropbox} this
30625          * @param {String} src
30626          */
30627         "beforeloadcanvas" : true,
30628         /**
30629          * @event trash
30630          * Fire when trash image
30631          * @param {Roo.bootstrap.UploadCropbox} this
30632          */
30633         "trash" : true,
30634         /**
30635          * @event download
30636          * Fire when download the image
30637          * @param {Roo.bootstrap.UploadCropbox} this
30638          */
30639         "download" : true,
30640         /**
30641          * @event footerbuttonclick
30642          * Fire when footerbuttonclick
30643          * @param {Roo.bootstrap.UploadCropbox} this
30644          * @param {String} type
30645          */
30646         "footerbuttonclick" : true,
30647         /**
30648          * @event resize
30649          * Fire when resize
30650          * @param {Roo.bootstrap.UploadCropbox} this
30651          */
30652         "resize" : true,
30653         /**
30654          * @event rotate
30655          * Fire when rotate the image
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          * @param {String} pos
30658          */
30659         "rotate" : true,
30660         /**
30661          * @event inspect
30662          * Fire when inspect the file
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          * @param {Object} file
30665          */
30666         "inspect" : true,
30667         /**
30668          * @event upload
30669          * Fire when xhr upload the file
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {Object} data
30672          */
30673         "upload" : true,
30674         /**
30675          * @event arrange
30676          * Fire when arrange the file data
30677          * @param {Roo.bootstrap.UploadCropbox} this
30678          * @param {Object} formData
30679          */
30680         "arrange" : true
30681     });
30682     
30683     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30684 };
30685
30686 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30687     
30688     emptyText : 'Click to upload image',
30689     rotateNotify : 'Image is too small to rotate',
30690     errorTimeout : 3000,
30691     scale : 0,
30692     baseScale : 1,
30693     rotate : 0,
30694     dragable : false,
30695     pinching : false,
30696     mouseX : 0,
30697     mouseY : 0,
30698     cropData : false,
30699     minWidth : 300,
30700     minHeight : 300,
30701     file : false,
30702     exif : {},
30703     baseRotate : 1,
30704     cropType : 'image/jpeg',
30705     buttons : false,
30706     canvasLoaded : false,
30707     isDocument : false,
30708     method : 'POST',
30709     paramName : 'imageUpload',
30710     loadMask : true,
30711     loadingText : 'Loading...',
30712     maskEl : false,
30713     
30714     getAutoCreate : function()
30715     {
30716         var cfg = {
30717             tag : 'div',
30718             cls : 'roo-upload-cropbox',
30719             cn : [
30720                 {
30721                     tag : 'input',
30722                     cls : 'roo-upload-cropbox-selector',
30723                     type : 'file'
30724                 },
30725                 {
30726                     tag : 'div',
30727                     cls : 'roo-upload-cropbox-body',
30728                     style : 'cursor:pointer',
30729                     cn : [
30730                         {
30731                             tag : 'div',
30732                             cls : 'roo-upload-cropbox-preview'
30733                         },
30734                         {
30735                             tag : 'div',
30736                             cls : 'roo-upload-cropbox-thumb'
30737                         },
30738                         {
30739                             tag : 'div',
30740                             cls : 'roo-upload-cropbox-empty-notify',
30741                             html : this.emptyText
30742                         },
30743                         {
30744                             tag : 'div',
30745                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30746                             html : this.rotateNotify
30747                         }
30748                     ]
30749                 },
30750                 {
30751                     tag : 'div',
30752                     cls : 'roo-upload-cropbox-footer',
30753                     cn : {
30754                         tag : 'div',
30755                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30756                         cn : []
30757                     }
30758                 }
30759             ]
30760         };
30761         
30762         return cfg;
30763     },
30764     
30765     onRender : function(ct, position)
30766     {
30767         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30768         
30769         if (this.buttons.length) {
30770             
30771             Roo.each(this.buttons, function(bb) {
30772                 
30773                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30774                 
30775                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30776                 
30777             }, this);
30778         }
30779         
30780         if(this.loadMask){
30781             this.maskEl = this.el;
30782         }
30783     },
30784     
30785     initEvents : function()
30786     {
30787         this.urlAPI = (window.createObjectURL && window) || 
30788                                 (window.URL && URL.revokeObjectURL && URL) || 
30789                                 (window.webkitURL && webkitURL);
30790                         
30791         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30792         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30793         
30794         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30795         this.selectorEl.hide();
30796         
30797         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30798         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30799         
30800         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30801         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802         this.thumbEl.hide();
30803         
30804         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30805         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30806         
30807         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30808         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30809         this.errorEl.hide();
30810         
30811         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30812         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30813         this.footerEl.hide();
30814         
30815         this.setThumbBoxSize();
30816         
30817         this.bind();
30818         
30819         this.resize();
30820         
30821         this.fireEvent('initial', this);
30822     },
30823
30824     bind : function()
30825     {
30826         var _this = this;
30827         
30828         window.addEventListener("resize", function() { _this.resize(); } );
30829         
30830         this.bodyEl.on('click', this.beforeSelectFile, this);
30831         
30832         if(Roo.isTouch){
30833             this.bodyEl.on('touchstart', this.onTouchStart, this);
30834             this.bodyEl.on('touchmove', this.onTouchMove, this);
30835             this.bodyEl.on('touchend', this.onTouchEnd, this);
30836         }
30837         
30838         if(!Roo.isTouch){
30839             this.bodyEl.on('mousedown', this.onMouseDown, this);
30840             this.bodyEl.on('mousemove', this.onMouseMove, this);
30841             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30842             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30843             Roo.get(document).on('mouseup', this.onMouseUp, this);
30844         }
30845         
30846         this.selectorEl.on('change', this.onFileSelected, this);
30847     },
30848     
30849     reset : function()
30850     {    
30851         this.scale = 0;
30852         this.baseScale = 1;
30853         this.rotate = 0;
30854         this.baseRotate = 1;
30855         this.dragable = false;
30856         this.pinching = false;
30857         this.mouseX = 0;
30858         this.mouseY = 0;
30859         this.cropData = false;
30860         this.notifyEl.dom.innerHTML = this.emptyText;
30861         
30862         this.selectorEl.dom.value = '';
30863         
30864     },
30865     
30866     resize : function()
30867     {
30868         if(this.fireEvent('resize', this) != false){
30869             this.setThumbBoxPosition();
30870             this.setCanvasPosition();
30871         }
30872     },
30873     
30874     onFooterButtonClick : function(e, el, o, type)
30875     {
30876         switch (type) {
30877             case 'rotate-left' :
30878                 this.onRotateLeft(e);
30879                 break;
30880             case 'rotate-right' :
30881                 this.onRotateRight(e);
30882                 break;
30883             case 'picture' :
30884                 this.beforeSelectFile(e);
30885                 break;
30886             case 'trash' :
30887                 this.trash(e);
30888                 break;
30889             case 'crop' :
30890                 this.crop(e);
30891                 break;
30892             case 'download' :
30893                 this.download(e);
30894                 break;
30895             default :
30896                 break;
30897         }
30898         
30899         this.fireEvent('footerbuttonclick', this, type);
30900     },
30901     
30902     beforeSelectFile : function(e)
30903     {
30904         e.preventDefault();
30905         
30906         if(this.fireEvent('beforeselectfile', this) != false){
30907             this.selectorEl.dom.click();
30908         }
30909     },
30910     
30911     onFileSelected : function(e)
30912     {
30913         e.preventDefault();
30914         
30915         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30916             return;
30917         }
30918         
30919         var file = this.selectorEl.dom.files[0];
30920         
30921         if(this.fireEvent('inspect', this, file) != false){
30922             this.prepare(file);
30923         }
30924         
30925     },
30926     
30927     trash : function(e)
30928     {
30929         this.fireEvent('trash', this);
30930     },
30931     
30932     download : function(e)
30933     {
30934         this.fireEvent('download', this);
30935     },
30936     
30937     loadCanvas : function(src)
30938     {   
30939         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30940             
30941             this.reset();
30942             
30943             this.imageEl = document.createElement('img');
30944             
30945             var _this = this;
30946             
30947             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30948             
30949             this.imageEl.src = src;
30950         }
30951     },
30952     
30953     onLoadCanvas : function()
30954     {   
30955         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30956         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30957         
30958         this.bodyEl.un('click', this.beforeSelectFile, this);
30959         
30960         this.notifyEl.hide();
30961         this.thumbEl.show();
30962         this.footerEl.show();
30963         
30964         this.baseRotateLevel();
30965         
30966         if(this.isDocument){
30967             this.setThumbBoxSize();
30968         }
30969         
30970         this.setThumbBoxPosition();
30971         
30972         this.baseScaleLevel();
30973         
30974         this.draw();
30975         
30976         this.resize();
30977         
30978         this.canvasLoaded = true;
30979         
30980         if(this.loadMask){
30981             this.maskEl.unmask();
30982         }
30983         
30984     },
30985     
30986     setCanvasPosition : function()
30987     {   
30988         if(!this.canvasEl){
30989             return;
30990         }
30991         
30992         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30993         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30994         
30995         this.previewEl.setLeft(pw);
30996         this.previewEl.setTop(ph);
30997         
30998     },
30999     
31000     onMouseDown : function(e)
31001     {   
31002         e.stopEvent();
31003         
31004         this.dragable = true;
31005         this.pinching = false;
31006         
31007         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31008             this.dragable = false;
31009             return;
31010         }
31011         
31012         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31013         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31014         
31015     },
31016     
31017     onMouseMove : function(e)
31018     {   
31019         e.stopEvent();
31020         
31021         if(!this.canvasLoaded){
31022             return;
31023         }
31024         
31025         if (!this.dragable){
31026             return;
31027         }
31028         
31029         var minX = Math.ceil(this.thumbEl.getLeft(true));
31030         var minY = Math.ceil(this.thumbEl.getTop(true));
31031         
31032         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31033         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31034         
31035         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31036         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31037         
31038         x = x - this.mouseX;
31039         y = y - this.mouseY;
31040         
31041         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31042         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31043         
31044         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31045         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31046         
31047         this.previewEl.setLeft(bgX);
31048         this.previewEl.setTop(bgY);
31049         
31050         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31051         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31052     },
31053     
31054     onMouseUp : function(e)
31055     {   
31056         e.stopEvent();
31057         
31058         this.dragable = false;
31059     },
31060     
31061     onMouseWheel : function(e)
31062     {   
31063         e.stopEvent();
31064         
31065         this.startScale = this.scale;
31066         
31067         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31068         
31069         if(!this.zoomable()){
31070             this.scale = this.startScale;
31071             return;
31072         }
31073         
31074         this.draw();
31075         
31076         return;
31077     },
31078     
31079     zoomable : function()
31080     {
31081         var minScale = this.thumbEl.getWidth() / this.minWidth;
31082         
31083         if(this.minWidth < this.minHeight){
31084             minScale = this.thumbEl.getHeight() / this.minHeight;
31085         }
31086         
31087         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31088         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31089         
31090         if(
31091                 this.isDocument &&
31092                 (this.rotate == 0 || this.rotate == 180) && 
31093                 (
31094                     width > this.imageEl.OriginWidth || 
31095                     height > this.imageEl.OriginHeight ||
31096                     (width < this.minWidth && height < this.minHeight)
31097                 )
31098         ){
31099             return false;
31100         }
31101         
31102         if(
31103                 this.isDocument &&
31104                 (this.rotate == 90 || this.rotate == 270) && 
31105                 (
31106                     width > this.imageEl.OriginWidth || 
31107                     height > this.imageEl.OriginHeight ||
31108                     (width < this.minHeight && height < this.minWidth)
31109                 )
31110         ){
31111             return false;
31112         }
31113         
31114         if(
31115                 !this.isDocument &&
31116                 (this.rotate == 0 || this.rotate == 180) && 
31117                 (
31118                     width < this.minWidth || 
31119                     width > this.imageEl.OriginWidth || 
31120                     height < this.minHeight || 
31121                     height > this.imageEl.OriginHeight
31122                 )
31123         ){
31124             return false;
31125         }
31126         
31127         if(
31128                 !this.isDocument &&
31129                 (this.rotate == 90 || this.rotate == 270) && 
31130                 (
31131                     width < this.minHeight || 
31132                     width > this.imageEl.OriginWidth || 
31133                     height < this.minWidth || 
31134                     height > this.imageEl.OriginHeight
31135                 )
31136         ){
31137             return false;
31138         }
31139         
31140         return true;
31141         
31142     },
31143     
31144     onRotateLeft : function(e)
31145     {   
31146         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31147             
31148             var minScale = this.thumbEl.getWidth() / this.minWidth;
31149             
31150             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31151             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31152             
31153             this.startScale = this.scale;
31154             
31155             while (this.getScaleLevel() < minScale){
31156             
31157                 this.scale = this.scale + 1;
31158                 
31159                 if(!this.zoomable()){
31160                     break;
31161                 }
31162                 
31163                 if(
31164                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31165                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31166                 ){
31167                     continue;
31168                 }
31169                 
31170                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31171
31172                 this.draw();
31173                 
31174                 return;
31175             }
31176             
31177             this.scale = this.startScale;
31178             
31179             this.onRotateFail();
31180             
31181             return false;
31182         }
31183         
31184         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31185
31186         if(this.isDocument){
31187             this.setThumbBoxSize();
31188             this.setThumbBoxPosition();
31189             this.setCanvasPosition();
31190         }
31191         
31192         this.draw();
31193         
31194         this.fireEvent('rotate', this, 'left');
31195         
31196     },
31197     
31198     onRotateRight : function(e)
31199     {
31200         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31201             
31202             var minScale = this.thumbEl.getWidth() / this.minWidth;
31203         
31204             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31205             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31206             
31207             this.startScale = this.scale;
31208             
31209             while (this.getScaleLevel() < minScale){
31210             
31211                 this.scale = this.scale + 1;
31212                 
31213                 if(!this.zoomable()){
31214                     break;
31215                 }
31216                 
31217                 if(
31218                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31219                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31220                 ){
31221                     continue;
31222                 }
31223                 
31224                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31225
31226                 this.draw();
31227                 
31228                 return;
31229             }
31230             
31231             this.scale = this.startScale;
31232             
31233             this.onRotateFail();
31234             
31235             return false;
31236         }
31237         
31238         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31239
31240         if(this.isDocument){
31241             this.setThumbBoxSize();
31242             this.setThumbBoxPosition();
31243             this.setCanvasPosition();
31244         }
31245         
31246         this.draw();
31247         
31248         this.fireEvent('rotate', this, 'right');
31249     },
31250     
31251     onRotateFail : function()
31252     {
31253         this.errorEl.show(true);
31254         
31255         var _this = this;
31256         
31257         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31258     },
31259     
31260     draw : function()
31261     {
31262         this.previewEl.dom.innerHTML = '';
31263         
31264         var canvasEl = document.createElement("canvas");
31265         
31266         var contextEl = canvasEl.getContext("2d");
31267         
31268         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31269         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31270         var center = this.imageEl.OriginWidth / 2;
31271         
31272         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31273             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31274             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31275             center = this.imageEl.OriginHeight / 2;
31276         }
31277         
31278         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31279         
31280         contextEl.translate(center, center);
31281         contextEl.rotate(this.rotate * Math.PI / 180);
31282
31283         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31284         
31285         this.canvasEl = document.createElement("canvas");
31286         
31287         this.contextEl = this.canvasEl.getContext("2d");
31288         
31289         switch (this.rotate) {
31290             case 0 :
31291                 
31292                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31293                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31294                 
31295                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31296                 
31297                 break;
31298             case 90 : 
31299                 
31300                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31301                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31302                 
31303                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31304                     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);
31305                     break;
31306                 }
31307                 
31308                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31309                 
31310                 break;
31311             case 180 :
31312                 
31313                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31314                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31315                 
31316                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31317                     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);
31318                     break;
31319                 }
31320                 
31321                 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);
31322                 
31323                 break;
31324             case 270 :
31325                 
31326                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31327                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31328         
31329                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31330                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31331                     break;
31332                 }
31333                 
31334                 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);
31335                 
31336                 break;
31337             default : 
31338                 break;
31339         }
31340         
31341         this.previewEl.appendChild(this.canvasEl);
31342         
31343         this.setCanvasPosition();
31344     },
31345     
31346     crop : function()
31347     {
31348         if(!this.canvasLoaded){
31349             return;
31350         }
31351         
31352         var imageCanvas = document.createElement("canvas");
31353         
31354         var imageContext = imageCanvas.getContext("2d");
31355         
31356         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31357         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31358         
31359         var center = imageCanvas.width / 2;
31360         
31361         imageContext.translate(center, center);
31362         
31363         imageContext.rotate(this.rotate * Math.PI / 180);
31364         
31365         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31366         
31367         var canvas = document.createElement("canvas");
31368         
31369         var context = canvas.getContext("2d");
31370                 
31371         canvas.width = this.minWidth;
31372         canvas.height = this.minHeight;
31373
31374         switch (this.rotate) {
31375             case 0 :
31376                 
31377                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31378                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31379                 
31380                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31381                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31382                 
31383                 var targetWidth = this.minWidth - 2 * x;
31384                 var targetHeight = this.minHeight - 2 * y;
31385                 
31386                 var scale = 1;
31387                 
31388                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31389                     scale = targetWidth / width;
31390                 }
31391                 
31392                 if(x > 0 && y == 0){
31393                     scale = targetHeight / height;
31394                 }
31395                 
31396                 if(x > 0 && y > 0){
31397                     scale = targetWidth / width;
31398                     
31399                     if(width < height){
31400                         scale = targetHeight / height;
31401                     }
31402                 }
31403                 
31404                 context.scale(scale, scale);
31405                 
31406                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31407                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31408
31409                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31410                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31411
31412                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31413                 
31414                 break;
31415             case 90 : 
31416                 
31417                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31418                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31419                 
31420                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31421                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31422                 
31423                 var targetWidth = this.minWidth - 2 * x;
31424                 var targetHeight = this.minHeight - 2 * y;
31425                 
31426                 var scale = 1;
31427                 
31428                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31429                     scale = targetWidth / width;
31430                 }
31431                 
31432                 if(x > 0 && y == 0){
31433                     scale = targetHeight / height;
31434                 }
31435                 
31436                 if(x > 0 && y > 0){
31437                     scale = targetWidth / width;
31438                     
31439                     if(width < height){
31440                         scale = targetHeight / height;
31441                     }
31442                 }
31443                 
31444                 context.scale(scale, scale);
31445                 
31446                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31447                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31448
31449                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31450                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31451                 
31452                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31453                 
31454                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31455                 
31456                 break;
31457             case 180 :
31458                 
31459                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31460                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31461                 
31462                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31463                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31464                 
31465                 var targetWidth = this.minWidth - 2 * x;
31466                 var targetHeight = this.minHeight - 2 * y;
31467                 
31468                 var scale = 1;
31469                 
31470                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31471                     scale = targetWidth / width;
31472                 }
31473                 
31474                 if(x > 0 && y == 0){
31475                     scale = targetHeight / height;
31476                 }
31477                 
31478                 if(x > 0 && y > 0){
31479                     scale = targetWidth / width;
31480                     
31481                     if(width < height){
31482                         scale = targetHeight / height;
31483                     }
31484                 }
31485                 
31486                 context.scale(scale, scale);
31487                 
31488                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31489                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31490
31491                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31492                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31493
31494                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31495                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31496                 
31497                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31498                 
31499                 break;
31500             case 270 :
31501                 
31502                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31503                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31504                 
31505                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31506                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31507                 
31508                 var targetWidth = this.minWidth - 2 * x;
31509                 var targetHeight = this.minHeight - 2 * y;
31510                 
31511                 var scale = 1;
31512                 
31513                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31514                     scale = targetWidth / width;
31515                 }
31516                 
31517                 if(x > 0 && y == 0){
31518                     scale = targetHeight / height;
31519                 }
31520                 
31521                 if(x > 0 && y > 0){
31522                     scale = targetWidth / width;
31523                     
31524                     if(width < height){
31525                         scale = targetHeight / height;
31526                     }
31527                 }
31528                 
31529                 context.scale(scale, scale);
31530                 
31531                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31532                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31533
31534                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31535                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31536                 
31537                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31538                 
31539                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31540                 
31541                 break;
31542             default : 
31543                 break;
31544         }
31545         
31546         this.cropData = canvas.toDataURL(this.cropType);
31547         
31548         if(this.fireEvent('crop', this, this.cropData) !== false){
31549             this.process(this.file, this.cropData);
31550         }
31551         
31552         return;
31553         
31554     },
31555     
31556     setThumbBoxSize : function()
31557     {
31558         var width, height;
31559         
31560         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31561             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31562             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31563             
31564             this.minWidth = width;
31565             this.minHeight = height;
31566             
31567             if(this.rotate == 90 || this.rotate == 270){
31568                 this.minWidth = height;
31569                 this.minHeight = width;
31570             }
31571         }
31572         
31573         height = 300;
31574         width = Math.ceil(this.minWidth * height / this.minHeight);
31575         
31576         if(this.minWidth > this.minHeight){
31577             width = 300;
31578             height = Math.ceil(this.minHeight * width / this.minWidth);
31579         }
31580         
31581         this.thumbEl.setStyle({
31582             width : width + 'px',
31583             height : height + 'px'
31584         });
31585
31586         return;
31587             
31588     },
31589     
31590     setThumbBoxPosition : function()
31591     {
31592         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31593         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31594         
31595         this.thumbEl.setLeft(x);
31596         this.thumbEl.setTop(y);
31597         
31598     },
31599     
31600     baseRotateLevel : function()
31601     {
31602         this.baseRotate = 1;
31603         
31604         if(
31605                 typeof(this.exif) != 'undefined' &&
31606                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31607                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31608         ){
31609             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31610         }
31611         
31612         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31613         
31614     },
31615     
31616     baseScaleLevel : function()
31617     {
31618         var width, height;
31619         
31620         if(this.isDocument){
31621             
31622             if(this.baseRotate == 6 || this.baseRotate == 8){
31623             
31624                 height = this.thumbEl.getHeight();
31625                 this.baseScale = height / this.imageEl.OriginWidth;
31626
31627                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31628                     width = this.thumbEl.getWidth();
31629                     this.baseScale = width / this.imageEl.OriginHeight;
31630                 }
31631
31632                 return;
31633             }
31634
31635             height = this.thumbEl.getHeight();
31636             this.baseScale = height / this.imageEl.OriginHeight;
31637
31638             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31639                 width = this.thumbEl.getWidth();
31640                 this.baseScale = width / this.imageEl.OriginWidth;
31641             }
31642
31643             return;
31644         }
31645         
31646         if(this.baseRotate == 6 || this.baseRotate == 8){
31647             
31648             width = this.thumbEl.getHeight();
31649             this.baseScale = width / this.imageEl.OriginHeight;
31650             
31651             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31652                 height = this.thumbEl.getWidth();
31653                 this.baseScale = height / this.imageEl.OriginHeight;
31654             }
31655             
31656             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31657                 height = this.thumbEl.getWidth();
31658                 this.baseScale = height / this.imageEl.OriginHeight;
31659                 
31660                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31661                     width = this.thumbEl.getHeight();
31662                     this.baseScale = width / this.imageEl.OriginWidth;
31663                 }
31664             }
31665             
31666             return;
31667         }
31668         
31669         width = this.thumbEl.getWidth();
31670         this.baseScale = width / this.imageEl.OriginWidth;
31671         
31672         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31673             height = this.thumbEl.getHeight();
31674             this.baseScale = height / this.imageEl.OriginHeight;
31675         }
31676         
31677         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31678             
31679             height = this.thumbEl.getHeight();
31680             this.baseScale = height / this.imageEl.OriginHeight;
31681             
31682             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31683                 width = this.thumbEl.getWidth();
31684                 this.baseScale = width / this.imageEl.OriginWidth;
31685             }
31686             
31687         }
31688         
31689         return;
31690     },
31691     
31692     getScaleLevel : function()
31693     {
31694         return this.baseScale * Math.pow(1.1, this.scale);
31695     },
31696     
31697     onTouchStart : function(e)
31698     {
31699         if(!this.canvasLoaded){
31700             this.beforeSelectFile(e);
31701             return;
31702         }
31703         
31704         var touches = e.browserEvent.touches;
31705         
31706         if(!touches){
31707             return;
31708         }
31709         
31710         if(touches.length == 1){
31711             this.onMouseDown(e);
31712             return;
31713         }
31714         
31715         if(touches.length != 2){
31716             return;
31717         }
31718         
31719         var coords = [];
31720         
31721         for(var i = 0, finger; finger = touches[i]; i++){
31722             coords.push(finger.pageX, finger.pageY);
31723         }
31724         
31725         var x = Math.pow(coords[0] - coords[2], 2);
31726         var y = Math.pow(coords[1] - coords[3], 2);
31727         
31728         this.startDistance = Math.sqrt(x + y);
31729         
31730         this.startScale = this.scale;
31731         
31732         this.pinching = true;
31733         this.dragable = false;
31734         
31735     },
31736     
31737     onTouchMove : function(e)
31738     {
31739         if(!this.pinching && !this.dragable){
31740             return;
31741         }
31742         
31743         var touches = e.browserEvent.touches;
31744         
31745         if(!touches){
31746             return;
31747         }
31748         
31749         if(this.dragable){
31750             this.onMouseMove(e);
31751             return;
31752         }
31753         
31754         var coords = [];
31755         
31756         for(var i = 0, finger; finger = touches[i]; i++){
31757             coords.push(finger.pageX, finger.pageY);
31758         }
31759         
31760         var x = Math.pow(coords[0] - coords[2], 2);
31761         var y = Math.pow(coords[1] - coords[3], 2);
31762         
31763         this.endDistance = Math.sqrt(x + y);
31764         
31765         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31766         
31767         if(!this.zoomable()){
31768             this.scale = this.startScale;
31769             return;
31770         }
31771         
31772         this.draw();
31773         
31774     },
31775     
31776     onTouchEnd : function(e)
31777     {
31778         this.pinching = false;
31779         this.dragable = false;
31780         
31781     },
31782     
31783     process : function(file, crop)
31784     {
31785         if(this.loadMask){
31786             this.maskEl.mask(this.loadingText);
31787         }
31788         
31789         this.xhr = new XMLHttpRequest();
31790         
31791         file.xhr = this.xhr;
31792
31793         this.xhr.open(this.method, this.url, true);
31794         
31795         var headers = {
31796             "Accept": "application/json",
31797             "Cache-Control": "no-cache",
31798             "X-Requested-With": "XMLHttpRequest"
31799         };
31800         
31801         for (var headerName in headers) {
31802             var headerValue = headers[headerName];
31803             if (headerValue) {
31804                 this.xhr.setRequestHeader(headerName, headerValue);
31805             }
31806         }
31807         
31808         var _this = this;
31809         
31810         this.xhr.onload = function()
31811         {
31812             _this.xhrOnLoad(_this.xhr);
31813         }
31814         
31815         this.xhr.onerror = function()
31816         {
31817             _this.xhrOnError(_this.xhr);
31818         }
31819         
31820         var formData = new FormData();
31821
31822         formData.append('returnHTML', 'NO');
31823         
31824         if(crop){
31825             formData.append('crop', crop);
31826         }
31827         
31828         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31829             formData.append(this.paramName, file, file.name);
31830         }
31831         
31832         if(typeof(file.filename) != 'undefined'){
31833             formData.append('filename', file.filename);
31834         }
31835         
31836         if(typeof(file.mimetype) != 'undefined'){
31837             formData.append('mimetype', file.mimetype);
31838         }
31839         
31840         if(this.fireEvent('arrange', this, formData) != false){
31841             this.xhr.send(formData);
31842         };
31843     },
31844     
31845     xhrOnLoad : function(xhr)
31846     {
31847         if(this.loadMask){
31848             this.maskEl.unmask();
31849         }
31850         
31851         if (xhr.readyState !== 4) {
31852             this.fireEvent('exception', this, xhr);
31853             return;
31854         }
31855
31856         var response = Roo.decode(xhr.responseText);
31857         
31858         if(!response.success){
31859             this.fireEvent('exception', this, xhr);
31860             return;
31861         }
31862         
31863         var response = Roo.decode(xhr.responseText);
31864         
31865         this.fireEvent('upload', this, response);
31866         
31867     },
31868     
31869     xhrOnError : function()
31870     {
31871         if(this.loadMask){
31872             this.maskEl.unmask();
31873         }
31874         
31875         Roo.log('xhr on error');
31876         
31877         var response = Roo.decode(xhr.responseText);
31878           
31879         Roo.log(response);
31880         
31881     },
31882     
31883     prepare : function(file)
31884     {   
31885         if(this.loadMask){
31886             this.maskEl.mask(this.loadingText);
31887         }
31888         
31889         this.file = false;
31890         this.exif = {};
31891         
31892         if(typeof(file) === 'string'){
31893             this.loadCanvas(file);
31894             return;
31895         }
31896         
31897         if(!file || !this.urlAPI){
31898             return;
31899         }
31900         
31901         this.file = file;
31902         this.cropType = file.type;
31903         
31904         var _this = this;
31905         
31906         if(this.fireEvent('prepare', this, this.file) != false){
31907             
31908             var reader = new FileReader();
31909             
31910             reader.onload = function (e) {
31911                 if (e.target.error) {
31912                     Roo.log(e.target.error);
31913                     return;
31914                 }
31915                 
31916                 var buffer = e.target.result,
31917                     dataView = new DataView(buffer),
31918                     offset = 2,
31919                     maxOffset = dataView.byteLength - 4,
31920                     markerBytes,
31921                     markerLength;
31922                 
31923                 if (dataView.getUint16(0) === 0xffd8) {
31924                     while (offset < maxOffset) {
31925                         markerBytes = dataView.getUint16(offset);
31926                         
31927                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31928                             markerLength = dataView.getUint16(offset + 2) + 2;
31929                             if (offset + markerLength > dataView.byteLength) {
31930                                 Roo.log('Invalid meta data: Invalid segment size.');
31931                                 break;
31932                             }
31933                             
31934                             if(markerBytes == 0xffe1){
31935                                 _this.parseExifData(
31936                                     dataView,
31937                                     offset,
31938                                     markerLength
31939                                 );
31940                             }
31941                             
31942                             offset += markerLength;
31943                             
31944                             continue;
31945                         }
31946                         
31947                         break;
31948                     }
31949                     
31950                 }
31951                 
31952                 var url = _this.urlAPI.createObjectURL(_this.file);
31953                 
31954                 _this.loadCanvas(url);
31955                 
31956                 return;
31957             }
31958             
31959             reader.readAsArrayBuffer(this.file);
31960             
31961         }
31962         
31963     },
31964     
31965     parseExifData : function(dataView, offset, length)
31966     {
31967         var tiffOffset = offset + 10,
31968             littleEndian,
31969             dirOffset;
31970     
31971         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31972             // No Exif data, might be XMP data instead
31973             return;
31974         }
31975         
31976         // Check for the ASCII code for "Exif" (0x45786966):
31977         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31978             // No Exif data, might be XMP data instead
31979             return;
31980         }
31981         if (tiffOffset + 8 > dataView.byteLength) {
31982             Roo.log('Invalid Exif data: Invalid segment size.');
31983             return;
31984         }
31985         // Check for the two null bytes:
31986         if (dataView.getUint16(offset + 8) !== 0x0000) {
31987             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31988             return;
31989         }
31990         // Check the byte alignment:
31991         switch (dataView.getUint16(tiffOffset)) {
31992         case 0x4949:
31993             littleEndian = true;
31994             break;
31995         case 0x4D4D:
31996             littleEndian = false;
31997             break;
31998         default:
31999             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32000             return;
32001         }
32002         // Check for the TIFF tag marker (0x002A):
32003         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32004             Roo.log('Invalid Exif data: Missing TIFF marker.');
32005             return;
32006         }
32007         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32008         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32009         
32010         this.parseExifTags(
32011             dataView,
32012             tiffOffset,
32013             tiffOffset + dirOffset,
32014             littleEndian
32015         );
32016     },
32017     
32018     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32019     {
32020         var tagsNumber,
32021             dirEndOffset,
32022             i;
32023         if (dirOffset + 6 > dataView.byteLength) {
32024             Roo.log('Invalid Exif data: Invalid directory offset.');
32025             return;
32026         }
32027         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32028         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32029         if (dirEndOffset + 4 > dataView.byteLength) {
32030             Roo.log('Invalid Exif data: Invalid directory size.');
32031             return;
32032         }
32033         for (i = 0; i < tagsNumber; i += 1) {
32034             this.parseExifTag(
32035                 dataView,
32036                 tiffOffset,
32037                 dirOffset + 2 + 12 * i, // tag offset
32038                 littleEndian
32039             );
32040         }
32041         // Return the offset to the next directory:
32042         return dataView.getUint32(dirEndOffset, littleEndian);
32043     },
32044     
32045     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32046     {
32047         var tag = dataView.getUint16(offset, littleEndian);
32048         
32049         this.exif[tag] = this.getExifValue(
32050             dataView,
32051             tiffOffset,
32052             offset,
32053             dataView.getUint16(offset + 2, littleEndian), // tag type
32054             dataView.getUint32(offset + 4, littleEndian), // tag length
32055             littleEndian
32056         );
32057     },
32058     
32059     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32060     {
32061         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32062             tagSize,
32063             dataOffset,
32064             values,
32065             i,
32066             str,
32067             c;
32068     
32069         if (!tagType) {
32070             Roo.log('Invalid Exif data: Invalid tag type.');
32071             return;
32072         }
32073         
32074         tagSize = tagType.size * length;
32075         // Determine if the value is contained in the dataOffset bytes,
32076         // or if the value at the dataOffset is a pointer to the actual data:
32077         dataOffset = tagSize > 4 ?
32078                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32079         if (dataOffset + tagSize > dataView.byteLength) {
32080             Roo.log('Invalid Exif data: Invalid data offset.');
32081             return;
32082         }
32083         if (length === 1) {
32084             return tagType.getValue(dataView, dataOffset, littleEndian);
32085         }
32086         values = [];
32087         for (i = 0; i < length; i += 1) {
32088             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32089         }
32090         
32091         if (tagType.ascii) {
32092             str = '';
32093             // Concatenate the chars:
32094             for (i = 0; i < values.length; i += 1) {
32095                 c = values[i];
32096                 // Ignore the terminating NULL byte(s):
32097                 if (c === '\u0000') {
32098                     break;
32099                 }
32100                 str += c;
32101             }
32102             return str;
32103         }
32104         return values;
32105     }
32106     
32107 });
32108
32109 Roo.apply(Roo.bootstrap.UploadCropbox, {
32110     tags : {
32111         'Orientation': 0x0112
32112     },
32113     
32114     Orientation: {
32115             1: 0, //'top-left',
32116 //            2: 'top-right',
32117             3: 180, //'bottom-right',
32118 //            4: 'bottom-left',
32119 //            5: 'left-top',
32120             6: 90, //'right-top',
32121 //            7: 'right-bottom',
32122             8: 270 //'left-bottom'
32123     },
32124     
32125     exifTagTypes : {
32126         // byte, 8-bit unsigned int:
32127         1: {
32128             getValue: function (dataView, dataOffset) {
32129                 return dataView.getUint8(dataOffset);
32130             },
32131             size: 1
32132         },
32133         // ascii, 8-bit byte:
32134         2: {
32135             getValue: function (dataView, dataOffset) {
32136                 return String.fromCharCode(dataView.getUint8(dataOffset));
32137             },
32138             size: 1,
32139             ascii: true
32140         },
32141         // short, 16 bit int:
32142         3: {
32143             getValue: function (dataView, dataOffset, littleEndian) {
32144                 return dataView.getUint16(dataOffset, littleEndian);
32145             },
32146             size: 2
32147         },
32148         // long, 32 bit int:
32149         4: {
32150             getValue: function (dataView, dataOffset, littleEndian) {
32151                 return dataView.getUint32(dataOffset, littleEndian);
32152             },
32153             size: 4
32154         },
32155         // rational = two long values, first is numerator, second is denominator:
32156         5: {
32157             getValue: function (dataView, dataOffset, littleEndian) {
32158                 return dataView.getUint32(dataOffset, littleEndian) /
32159                     dataView.getUint32(dataOffset + 4, littleEndian);
32160             },
32161             size: 8
32162         },
32163         // slong, 32 bit signed int:
32164         9: {
32165             getValue: function (dataView, dataOffset, littleEndian) {
32166                 return dataView.getInt32(dataOffset, littleEndian);
32167             },
32168             size: 4
32169         },
32170         // srational, two slongs, first is numerator, second is denominator:
32171         10: {
32172             getValue: function (dataView, dataOffset, littleEndian) {
32173                 return dataView.getInt32(dataOffset, littleEndian) /
32174                     dataView.getInt32(dataOffset + 4, littleEndian);
32175             },
32176             size: 8
32177         }
32178     },
32179     
32180     footer : {
32181         STANDARD : [
32182             {
32183                 tag : 'div',
32184                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32185                 action : 'rotate-left',
32186                 cn : [
32187                     {
32188                         tag : 'button',
32189                         cls : 'btn btn-default',
32190                         html : '<i class="fa fa-undo"></i>'
32191                     }
32192                 ]
32193             },
32194             {
32195                 tag : 'div',
32196                 cls : 'btn-group roo-upload-cropbox-picture',
32197                 action : 'picture',
32198                 cn : [
32199                     {
32200                         tag : 'button',
32201                         cls : 'btn btn-default',
32202                         html : '<i class="fa fa-picture-o"></i>'
32203                     }
32204                 ]
32205             },
32206             {
32207                 tag : 'div',
32208                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32209                 action : 'rotate-right',
32210                 cn : [
32211                     {
32212                         tag : 'button',
32213                         cls : 'btn btn-default',
32214                         html : '<i class="fa fa-repeat"></i>'
32215                     }
32216                 ]
32217             }
32218         ],
32219         DOCUMENT : [
32220             {
32221                 tag : 'div',
32222                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32223                 action : 'rotate-left',
32224                 cn : [
32225                     {
32226                         tag : 'button',
32227                         cls : 'btn btn-default',
32228                         html : '<i class="fa fa-undo"></i>'
32229                     }
32230                 ]
32231             },
32232             {
32233                 tag : 'div',
32234                 cls : 'btn-group roo-upload-cropbox-download',
32235                 action : 'download',
32236                 cn : [
32237                     {
32238                         tag : 'button',
32239                         cls : 'btn btn-default',
32240                         html : '<i class="fa fa-download"></i>'
32241                     }
32242                 ]
32243             },
32244             {
32245                 tag : 'div',
32246                 cls : 'btn-group roo-upload-cropbox-crop',
32247                 action : 'crop',
32248                 cn : [
32249                     {
32250                         tag : 'button',
32251                         cls : 'btn btn-default',
32252                         html : '<i class="fa fa-crop"></i>'
32253                     }
32254                 ]
32255             },
32256             {
32257                 tag : 'div',
32258                 cls : 'btn-group roo-upload-cropbox-trash',
32259                 action : 'trash',
32260                 cn : [
32261                     {
32262                         tag : 'button',
32263                         cls : 'btn btn-default',
32264                         html : '<i class="fa fa-trash"></i>'
32265                     }
32266                 ]
32267             },
32268             {
32269                 tag : 'div',
32270                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32271                 action : 'rotate-right',
32272                 cn : [
32273                     {
32274                         tag : 'button',
32275                         cls : 'btn btn-default',
32276                         html : '<i class="fa fa-repeat"></i>'
32277                     }
32278                 ]
32279             }
32280         ],
32281         ROTATOR : [
32282             {
32283                 tag : 'div',
32284                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32285                 action : 'rotate-left',
32286                 cn : [
32287                     {
32288                         tag : 'button',
32289                         cls : 'btn btn-default',
32290                         html : '<i class="fa fa-undo"></i>'
32291                     }
32292                 ]
32293             },
32294             {
32295                 tag : 'div',
32296                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32297                 action : 'rotate-right',
32298                 cn : [
32299                     {
32300                         tag : 'button',
32301                         cls : 'btn btn-default',
32302                         html : '<i class="fa fa-repeat"></i>'
32303                     }
32304                 ]
32305             }
32306         ]
32307     }
32308 });
32309
32310 /*
32311 * Licence: LGPL
32312 */
32313
32314 /**
32315  * @class Roo.bootstrap.DocumentManager
32316  * @extends Roo.bootstrap.Component
32317  * Bootstrap DocumentManager class
32318  * @cfg {String} paramName default 'imageUpload'
32319  * @cfg {String} toolTipName default 'filename'
32320  * @cfg {String} method default POST
32321  * @cfg {String} url action url
32322  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32323  * @cfg {Boolean} multiple multiple upload default true
32324  * @cfg {Number} thumbSize default 300
32325  * @cfg {String} fieldLabel
32326  * @cfg {Number} labelWidth default 4
32327  * @cfg {String} labelAlign (left|top) default left
32328  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32329 * @cfg {Number} labellg set the width of label (1-12)
32330  * @cfg {Number} labelmd set the width of label (1-12)
32331  * @cfg {Number} labelsm set the width of label (1-12)
32332  * @cfg {Number} labelxs set the width of label (1-12)
32333  * 
32334  * @constructor
32335  * Create a new DocumentManager
32336  * @param {Object} config The config object
32337  */
32338
32339 Roo.bootstrap.DocumentManager = function(config){
32340     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32341     
32342     this.files = [];
32343     this.delegates = [];
32344     
32345     this.addEvents({
32346         /**
32347          * @event initial
32348          * Fire when initial the DocumentManager
32349          * @param {Roo.bootstrap.DocumentManager} this
32350          */
32351         "initial" : true,
32352         /**
32353          * @event inspect
32354          * inspect selected file
32355          * @param {Roo.bootstrap.DocumentManager} this
32356          * @param {File} file
32357          */
32358         "inspect" : true,
32359         /**
32360          * @event exception
32361          * Fire when xhr load exception
32362          * @param {Roo.bootstrap.DocumentManager} this
32363          * @param {XMLHttpRequest} xhr
32364          */
32365         "exception" : true,
32366         /**
32367          * @event afterupload
32368          * Fire when xhr load exception
32369          * @param {Roo.bootstrap.DocumentManager} this
32370          * @param {XMLHttpRequest} xhr
32371          */
32372         "afterupload" : true,
32373         /**
32374          * @event prepare
32375          * prepare the form data
32376          * @param {Roo.bootstrap.DocumentManager} this
32377          * @param {Object} formData
32378          */
32379         "prepare" : true,
32380         /**
32381          * @event remove
32382          * Fire when remove the file
32383          * @param {Roo.bootstrap.DocumentManager} this
32384          * @param {Object} file
32385          */
32386         "remove" : true,
32387         /**
32388          * @event refresh
32389          * Fire after refresh the file
32390          * @param {Roo.bootstrap.DocumentManager} this
32391          */
32392         "refresh" : true,
32393         /**
32394          * @event click
32395          * Fire after click the image
32396          * @param {Roo.bootstrap.DocumentManager} this
32397          * @param {Object} file
32398          */
32399         "click" : true,
32400         /**
32401          * @event edit
32402          * Fire when upload a image and editable set to true
32403          * @param {Roo.bootstrap.DocumentManager} this
32404          * @param {Object} file
32405          */
32406         "edit" : true,
32407         /**
32408          * @event beforeselectfile
32409          * Fire before select file
32410          * @param {Roo.bootstrap.DocumentManager} this
32411          */
32412         "beforeselectfile" : true,
32413         /**
32414          * @event process
32415          * Fire before process file
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          * @param {Object} file
32418          */
32419         "process" : true,
32420         /**
32421          * @event previewrendered
32422          * Fire when preview rendered
32423          * @param {Roo.bootstrap.DocumentManager} this
32424          * @param {Object} file
32425          */
32426         "previewrendered" : true,
32427         /**
32428          */
32429         "previewResize" : true
32430         
32431     });
32432 };
32433
32434 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32435     
32436     boxes : 0,
32437     inputName : '',
32438     thumbSize : 300,
32439     multiple : true,
32440     files : false,
32441     method : 'POST',
32442     url : '',
32443     paramName : 'imageUpload',
32444     toolTipName : 'filename',
32445     fieldLabel : '',
32446     labelWidth : 4,
32447     labelAlign : 'left',
32448     editable : true,
32449     delegates : false,
32450     xhr : false, 
32451     
32452     labellg : 0,
32453     labelmd : 0,
32454     labelsm : 0,
32455     labelxs : 0,
32456     
32457     getAutoCreate : function()
32458     {   
32459         var managerWidget = {
32460             tag : 'div',
32461             cls : 'roo-document-manager',
32462             cn : [
32463                 {
32464                     tag : 'input',
32465                     cls : 'roo-document-manager-selector',
32466                     type : 'file'
32467                 },
32468                 {
32469                     tag : 'div',
32470                     cls : 'roo-document-manager-uploader',
32471                     cn : [
32472                         {
32473                             tag : 'div',
32474                             cls : 'roo-document-manager-upload-btn',
32475                             html : '<i class="fa fa-plus"></i>'
32476                         }
32477                     ]
32478                     
32479                 }
32480             ]
32481         };
32482         
32483         var content = [
32484             {
32485                 tag : 'div',
32486                 cls : 'column col-md-12',
32487                 cn : managerWidget
32488             }
32489         ];
32490         
32491         if(this.fieldLabel.length){
32492             
32493             content = [
32494                 {
32495                     tag : 'div',
32496                     cls : 'column col-md-12',
32497                     html : this.fieldLabel
32498                 },
32499                 {
32500                     tag : 'div',
32501                     cls : 'column col-md-12',
32502                     cn : managerWidget
32503                 }
32504             ];
32505
32506             if(this.labelAlign == 'left'){
32507                 content = [
32508                     {
32509                         tag : 'div',
32510                         cls : 'column',
32511                         html : this.fieldLabel
32512                     },
32513                     {
32514                         tag : 'div',
32515                         cls : 'column',
32516                         cn : managerWidget
32517                     }
32518                 ];
32519                 
32520                 if(this.labelWidth > 12){
32521                     content[0].style = "width: " + this.labelWidth + 'px';
32522                 }
32523
32524                 if(this.labelWidth < 13 && this.labelmd == 0){
32525                     this.labelmd = this.labelWidth;
32526                 }
32527
32528                 if(this.labellg > 0){
32529                     content[0].cls += ' col-lg-' + this.labellg;
32530                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32531                 }
32532
32533                 if(this.labelmd > 0){
32534                     content[0].cls += ' col-md-' + this.labelmd;
32535                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32536                 }
32537
32538                 if(this.labelsm > 0){
32539                     content[0].cls += ' col-sm-' + this.labelsm;
32540                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32541                 }
32542
32543                 if(this.labelxs > 0){
32544                     content[0].cls += ' col-xs-' + this.labelxs;
32545                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32546                 }
32547                 
32548             }
32549         }
32550         
32551         var cfg = {
32552             tag : 'div',
32553             cls : 'row clearfix',
32554             cn : content
32555         };
32556         
32557         return cfg;
32558         
32559     },
32560     
32561     initEvents : function()
32562     {
32563         this.managerEl = this.el.select('.roo-document-manager', true).first();
32564         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32565         
32566         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32567         this.selectorEl.hide();
32568         
32569         if(this.multiple){
32570             this.selectorEl.attr('multiple', 'multiple');
32571         }
32572         
32573         this.selectorEl.on('change', this.onFileSelected, this);
32574         
32575         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32576         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32577         
32578         this.uploader.on('click', this.onUploaderClick, this);
32579         
32580         this.renderProgressDialog();
32581         
32582         var _this = this;
32583         
32584         window.addEventListener("resize", function() { _this.refresh(); } );
32585         
32586         this.fireEvent('initial', this);
32587     },
32588     
32589     renderProgressDialog : function()
32590     {
32591         var _this = this;
32592         
32593         this.progressDialog = new Roo.bootstrap.Modal({
32594             cls : 'roo-document-manager-progress-dialog',
32595             allow_close : false,
32596             animate : false,
32597             title : '',
32598             buttons : [
32599                 {
32600                     name  :'cancel',
32601                     weight : 'danger',
32602                     html : 'Cancel'
32603                 }
32604             ], 
32605             listeners : { 
32606                 btnclick : function() {
32607                     _this.uploadCancel();
32608                     this.hide();
32609                 }
32610             }
32611         });
32612          
32613         this.progressDialog.render(Roo.get(document.body));
32614          
32615         this.progress = new Roo.bootstrap.Progress({
32616             cls : 'roo-document-manager-progress',
32617             active : true,
32618             striped : true
32619         });
32620         
32621         this.progress.render(this.progressDialog.getChildContainer());
32622         
32623         this.progressBar = new Roo.bootstrap.ProgressBar({
32624             cls : 'roo-document-manager-progress-bar',
32625             aria_valuenow : 0,
32626             aria_valuemin : 0,
32627             aria_valuemax : 12,
32628             panel : 'success'
32629         });
32630         
32631         this.progressBar.render(this.progress.getChildContainer());
32632     },
32633     
32634     onUploaderClick : function(e)
32635     {
32636         e.preventDefault();
32637      
32638         if(this.fireEvent('beforeselectfile', this) != false){
32639             this.selectorEl.dom.click();
32640         }
32641         
32642     },
32643     
32644     onFileSelected : function(e)
32645     {
32646         e.preventDefault();
32647         
32648         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32649             return;
32650         }
32651         
32652         Roo.each(this.selectorEl.dom.files, function(file){
32653             if(this.fireEvent('inspect', this, file) != false){
32654                 this.files.push(file);
32655             }
32656         }, this);
32657         
32658         this.queue();
32659         
32660     },
32661     
32662     queue : function()
32663     {
32664         this.selectorEl.dom.value = '';
32665         
32666         if(!this.files || !this.files.length){
32667             return;
32668         }
32669         
32670         if(this.boxes > 0 && this.files.length > this.boxes){
32671             this.files = this.files.slice(0, this.boxes);
32672         }
32673         
32674         this.uploader.show();
32675         
32676         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32677             this.uploader.hide();
32678         }
32679         
32680         var _this = this;
32681         
32682         var files = [];
32683         
32684         var docs = [];
32685         
32686         Roo.each(this.files, function(file){
32687             
32688             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32689                 var f = this.renderPreview(file);
32690                 files.push(f);
32691                 return;
32692             }
32693             
32694             if(file.type.indexOf('image') != -1){
32695                 this.delegates.push(
32696                     (function(){
32697                         _this.process(file);
32698                     }).createDelegate(this)
32699                 );
32700         
32701                 return;
32702             }
32703             
32704             docs.push(
32705                 (function(){
32706                     _this.process(file);
32707                 }).createDelegate(this)
32708             );
32709             
32710         }, this);
32711         
32712         this.files = files;
32713         
32714         this.delegates = this.delegates.concat(docs);
32715         
32716         if(!this.delegates.length){
32717             this.refresh();
32718             return;
32719         }
32720         
32721         this.progressBar.aria_valuemax = this.delegates.length;
32722         
32723         this.arrange();
32724         
32725         return;
32726     },
32727     
32728     arrange : function()
32729     {
32730         if(!this.delegates.length){
32731             this.progressDialog.hide();
32732             this.refresh();
32733             return;
32734         }
32735         
32736         var delegate = this.delegates.shift();
32737         
32738         this.progressDialog.show();
32739         
32740         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32741         
32742         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32743         
32744         delegate();
32745     },
32746     
32747     refresh : function()
32748     {
32749         this.uploader.show();
32750         
32751         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32752             this.uploader.hide();
32753         }
32754         
32755         Roo.isTouch ? this.closable(false) : this.closable(true);
32756         
32757         this.fireEvent('refresh', this);
32758     },
32759     
32760     onRemove : function(e, el, o)
32761     {
32762         e.preventDefault();
32763         
32764         this.fireEvent('remove', this, o);
32765         
32766     },
32767     
32768     remove : function(o)
32769     {
32770         var files = [];
32771         
32772         Roo.each(this.files, function(file){
32773             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32774                 files.push(file);
32775                 return;
32776             }
32777
32778             o.target.remove();
32779
32780         }, this);
32781         
32782         this.files = files;
32783         
32784         this.refresh();
32785     },
32786     
32787     clear : function()
32788     {
32789         Roo.each(this.files, function(file){
32790             if(!file.target){
32791                 return;
32792             }
32793             
32794             file.target.remove();
32795
32796         }, this);
32797         
32798         this.files = [];
32799         
32800         this.refresh();
32801     },
32802     
32803     onClick : function(e, el, o)
32804     {
32805         e.preventDefault();
32806         
32807         this.fireEvent('click', this, o);
32808         
32809     },
32810     
32811     closable : function(closable)
32812     {
32813         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32814             
32815             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32816             
32817             if(closable){
32818                 el.show();
32819                 return;
32820             }
32821             
32822             el.hide();
32823             
32824         }, this);
32825     },
32826     
32827     xhrOnLoad : function(xhr)
32828     {
32829         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32830             el.remove();
32831         }, this);
32832         
32833         if (xhr.readyState !== 4) {
32834             this.arrange();
32835             this.fireEvent('exception', this, xhr);
32836             return;
32837         }
32838
32839         var response = Roo.decode(xhr.responseText);
32840         
32841         if(!response.success){
32842             this.arrange();
32843             this.fireEvent('exception', this, xhr);
32844             return;
32845         }
32846         
32847         var file = this.renderPreview(response.data);
32848         
32849         this.files.push(file);
32850         
32851         this.arrange();
32852         
32853         this.fireEvent('afterupload', this, xhr);
32854         
32855     },
32856     
32857     xhrOnError : function(xhr)
32858     {
32859         Roo.log('xhr on error');
32860         
32861         var response = Roo.decode(xhr.responseText);
32862           
32863         Roo.log(response);
32864         
32865         this.arrange();
32866     },
32867     
32868     process : function(file)
32869     {
32870         if(this.fireEvent('process', this, file) !== false){
32871             if(this.editable && file.type.indexOf('image') != -1){
32872                 this.fireEvent('edit', this, file);
32873                 return;
32874             }
32875
32876             this.uploadStart(file, false);
32877
32878             return;
32879         }
32880         
32881     },
32882     
32883     uploadStart : function(file, crop)
32884     {
32885         this.xhr = new XMLHttpRequest();
32886         
32887         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32888             this.arrange();
32889             return;
32890         }
32891         
32892         file.xhr = this.xhr;
32893             
32894         this.managerEl.createChild({
32895             tag : 'div',
32896             cls : 'roo-document-manager-loading',
32897             cn : [
32898                 {
32899                     tag : 'div',
32900                     tooltip : file.name,
32901                     cls : 'roo-document-manager-thumb',
32902                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32903                 }
32904             ]
32905
32906         });
32907
32908         this.xhr.open(this.method, this.url, true);
32909         
32910         var headers = {
32911             "Accept": "application/json",
32912             "Cache-Control": "no-cache",
32913             "X-Requested-With": "XMLHttpRequest"
32914         };
32915         
32916         for (var headerName in headers) {
32917             var headerValue = headers[headerName];
32918             if (headerValue) {
32919                 this.xhr.setRequestHeader(headerName, headerValue);
32920             }
32921         }
32922         
32923         var _this = this;
32924         
32925         this.xhr.onload = function()
32926         {
32927             _this.xhrOnLoad(_this.xhr);
32928         }
32929         
32930         this.xhr.onerror = function()
32931         {
32932             _this.xhrOnError(_this.xhr);
32933         }
32934         
32935         var formData = new FormData();
32936
32937         formData.append('returnHTML', 'NO');
32938         
32939         if(crop){
32940             formData.append('crop', crop);
32941         }
32942         
32943         formData.append(this.paramName, file, file.name);
32944         
32945         var options = {
32946             file : file, 
32947             manually : false
32948         };
32949         
32950         if(this.fireEvent('prepare', this, formData, options) != false){
32951             
32952             if(options.manually){
32953                 return;
32954             }
32955             
32956             this.xhr.send(formData);
32957             return;
32958         };
32959         
32960         this.uploadCancel();
32961     },
32962     
32963     uploadCancel : function()
32964     {
32965         if (this.xhr) {
32966             this.xhr.abort();
32967         }
32968         
32969         this.delegates = [];
32970         
32971         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32972             el.remove();
32973         }, this);
32974         
32975         this.arrange();
32976     },
32977     
32978     renderPreview : function(file)
32979     {
32980         if(typeof(file.target) != 'undefined' && file.target){
32981             return file;
32982         }
32983         
32984         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32985         
32986         var previewEl = this.managerEl.createChild({
32987             tag : 'div',
32988             cls : 'roo-document-manager-preview',
32989             cn : [
32990                 {
32991                     tag : 'div',
32992                     tooltip : file[this.toolTipName],
32993                     cls : 'roo-document-manager-thumb',
32994                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32995                 },
32996                 {
32997                     tag : 'button',
32998                     cls : 'close',
32999                     html : '<i class="fa fa-times-circle"></i>'
33000                 }
33001             ]
33002         });
33003
33004         var close = previewEl.select('button.close', true).first();
33005
33006         close.on('click', this.onRemove, this, file);
33007
33008         file.target = previewEl;
33009
33010         var image = previewEl.select('img', true).first();
33011         
33012         var _this = this;
33013         
33014         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33015         
33016         image.on('click', this.onClick, this, file);
33017         
33018         this.fireEvent('previewrendered', this, file);
33019         
33020         return file;
33021         
33022     },
33023     
33024     onPreviewLoad : function(file, image)
33025     {
33026         if(typeof(file.target) == 'undefined' || !file.target){
33027             return;
33028         }
33029         
33030         var width = image.dom.naturalWidth || image.dom.width;
33031         var height = image.dom.naturalHeight || image.dom.height;
33032         
33033         if(!this.previewResize) {
33034             return;
33035         }
33036         
33037         if(width > height){
33038             file.target.addClass('wide');
33039             return;
33040         }
33041         
33042         file.target.addClass('tall');
33043         return;
33044         
33045     },
33046     
33047     uploadFromSource : function(file, crop)
33048     {
33049         this.xhr = new XMLHttpRequest();
33050         
33051         this.managerEl.createChild({
33052             tag : 'div',
33053             cls : 'roo-document-manager-loading',
33054             cn : [
33055                 {
33056                     tag : 'div',
33057                     tooltip : file.name,
33058                     cls : 'roo-document-manager-thumb',
33059                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33060                 }
33061             ]
33062
33063         });
33064
33065         this.xhr.open(this.method, this.url, true);
33066         
33067         var headers = {
33068             "Accept": "application/json",
33069             "Cache-Control": "no-cache",
33070             "X-Requested-With": "XMLHttpRequest"
33071         };
33072         
33073         for (var headerName in headers) {
33074             var headerValue = headers[headerName];
33075             if (headerValue) {
33076                 this.xhr.setRequestHeader(headerName, headerValue);
33077             }
33078         }
33079         
33080         var _this = this;
33081         
33082         this.xhr.onload = function()
33083         {
33084             _this.xhrOnLoad(_this.xhr);
33085         }
33086         
33087         this.xhr.onerror = function()
33088         {
33089             _this.xhrOnError(_this.xhr);
33090         }
33091         
33092         var formData = new FormData();
33093
33094         formData.append('returnHTML', 'NO');
33095         
33096         formData.append('crop', crop);
33097         
33098         if(typeof(file.filename) != 'undefined'){
33099             formData.append('filename', file.filename);
33100         }
33101         
33102         if(typeof(file.mimetype) != 'undefined'){
33103             formData.append('mimetype', file.mimetype);
33104         }
33105         
33106         Roo.log(formData);
33107         
33108         if(this.fireEvent('prepare', this, formData) != false){
33109             this.xhr.send(formData);
33110         };
33111     }
33112 });
33113
33114 /*
33115 * Licence: LGPL
33116 */
33117
33118 /**
33119  * @class Roo.bootstrap.DocumentViewer
33120  * @extends Roo.bootstrap.Component
33121  * Bootstrap DocumentViewer class
33122  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33123  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33124  * 
33125  * @constructor
33126  * Create a new DocumentViewer
33127  * @param {Object} config The config object
33128  */
33129
33130 Roo.bootstrap.DocumentViewer = function(config){
33131     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33132     
33133     this.addEvents({
33134         /**
33135          * @event initial
33136          * Fire after initEvent
33137          * @param {Roo.bootstrap.DocumentViewer} this
33138          */
33139         "initial" : true,
33140         /**
33141          * @event click
33142          * Fire after click
33143          * @param {Roo.bootstrap.DocumentViewer} this
33144          */
33145         "click" : true,
33146         /**
33147          * @event download
33148          * Fire after download button
33149          * @param {Roo.bootstrap.DocumentViewer} this
33150          */
33151         "download" : true,
33152         /**
33153          * @event trash
33154          * Fire after trash button
33155          * @param {Roo.bootstrap.DocumentViewer} this
33156          */
33157         "trash" : true
33158         
33159     });
33160 };
33161
33162 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33163     
33164     showDownload : true,
33165     
33166     showTrash : true,
33167     
33168     getAutoCreate : function()
33169     {
33170         var cfg = {
33171             tag : 'div',
33172             cls : 'roo-document-viewer',
33173             cn : [
33174                 {
33175                     tag : 'div',
33176                     cls : 'roo-document-viewer-body',
33177                     cn : [
33178                         {
33179                             tag : 'div',
33180                             cls : 'roo-document-viewer-thumb',
33181                             cn : [
33182                                 {
33183                                     tag : 'img',
33184                                     cls : 'roo-document-viewer-image'
33185                                 }
33186                             ]
33187                         }
33188                     ]
33189                 },
33190                 {
33191                     tag : 'div',
33192                     cls : 'roo-document-viewer-footer',
33193                     cn : {
33194                         tag : 'div',
33195                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33196                         cn : [
33197                             {
33198                                 tag : 'div',
33199                                 cls : 'btn-group roo-document-viewer-download',
33200                                 cn : [
33201                                     {
33202                                         tag : 'button',
33203                                         cls : 'btn btn-default',
33204                                         html : '<i class="fa fa-download"></i>'
33205                                     }
33206                                 ]
33207                             },
33208                             {
33209                                 tag : 'div',
33210                                 cls : 'btn-group roo-document-viewer-trash',
33211                                 cn : [
33212                                     {
33213                                         tag : 'button',
33214                                         cls : 'btn btn-default',
33215                                         html : '<i class="fa fa-trash"></i>'
33216                                     }
33217                                 ]
33218                             }
33219                         ]
33220                     }
33221                 }
33222             ]
33223         };
33224         
33225         return cfg;
33226     },
33227     
33228     initEvents : function()
33229     {
33230         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33231         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33232         
33233         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33234         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33235         
33236         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33237         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33238         
33239         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33240         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33241         
33242         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33243         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33244         
33245         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33246         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33247         
33248         this.bodyEl.on('click', this.onClick, this);
33249         this.downloadBtn.on('click', this.onDownload, this);
33250         this.trashBtn.on('click', this.onTrash, this);
33251         
33252         this.downloadBtn.hide();
33253         this.trashBtn.hide();
33254         
33255         if(this.showDownload){
33256             this.downloadBtn.show();
33257         }
33258         
33259         if(this.showTrash){
33260             this.trashBtn.show();
33261         }
33262         
33263         if(!this.showDownload && !this.showTrash) {
33264             this.footerEl.hide();
33265         }
33266         
33267     },
33268     
33269     initial : function()
33270     {
33271         this.fireEvent('initial', this);
33272         
33273     },
33274     
33275     onClick : function(e)
33276     {
33277         e.preventDefault();
33278         
33279         this.fireEvent('click', this);
33280     },
33281     
33282     onDownload : function(e)
33283     {
33284         e.preventDefault();
33285         
33286         this.fireEvent('download', this);
33287     },
33288     
33289     onTrash : function(e)
33290     {
33291         e.preventDefault();
33292         
33293         this.fireEvent('trash', this);
33294     }
33295     
33296 });
33297 /*
33298  * - LGPL
33299  *
33300  * nav progress bar
33301  * 
33302  */
33303
33304 /**
33305  * @class Roo.bootstrap.NavProgressBar
33306  * @extends Roo.bootstrap.Component
33307  * Bootstrap NavProgressBar class
33308  * 
33309  * @constructor
33310  * Create a new nav progress bar
33311  * @param {Object} config The config object
33312  */
33313
33314 Roo.bootstrap.NavProgressBar = function(config){
33315     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33316
33317     this.bullets = this.bullets || [];
33318    
33319 //    Roo.bootstrap.NavProgressBar.register(this);
33320      this.addEvents({
33321         /**
33322              * @event changed
33323              * Fires when the active item changes
33324              * @param {Roo.bootstrap.NavProgressBar} this
33325              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33326              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33327          */
33328         'changed': true
33329      });
33330     
33331 };
33332
33333 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33334     
33335     bullets : [],
33336     barItems : [],
33337     
33338     getAutoCreate : function()
33339     {
33340         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33341         
33342         cfg = {
33343             tag : 'div',
33344             cls : 'roo-navigation-bar-group',
33345             cn : [
33346                 {
33347                     tag : 'div',
33348                     cls : 'roo-navigation-top-bar'
33349                 },
33350                 {
33351                     tag : 'div',
33352                     cls : 'roo-navigation-bullets-bar',
33353                     cn : [
33354                         {
33355                             tag : 'ul',
33356                             cls : 'roo-navigation-bar'
33357                         }
33358                     ]
33359                 },
33360                 
33361                 {
33362                     tag : 'div',
33363                     cls : 'roo-navigation-bottom-bar'
33364                 }
33365             ]
33366             
33367         };
33368         
33369         return cfg;
33370         
33371     },
33372     
33373     initEvents: function() 
33374     {
33375         
33376     },
33377     
33378     onRender : function(ct, position) 
33379     {
33380         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33381         
33382         if(this.bullets.length){
33383             Roo.each(this.bullets, function(b){
33384                this.addItem(b);
33385             }, this);
33386         }
33387         
33388         this.format();
33389         
33390     },
33391     
33392     addItem : function(cfg)
33393     {
33394         var item = new Roo.bootstrap.NavProgressItem(cfg);
33395         
33396         item.parentId = this.id;
33397         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33398         
33399         if(cfg.html){
33400             var top = new Roo.bootstrap.Element({
33401                 tag : 'div',
33402                 cls : 'roo-navigation-bar-text'
33403             });
33404             
33405             var bottom = new Roo.bootstrap.Element({
33406                 tag : 'div',
33407                 cls : 'roo-navigation-bar-text'
33408             });
33409             
33410             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33411             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33412             
33413             var topText = new Roo.bootstrap.Element({
33414                 tag : 'span',
33415                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33416             });
33417             
33418             var bottomText = new Roo.bootstrap.Element({
33419                 tag : 'span',
33420                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33421             });
33422             
33423             topText.onRender(top.el, null);
33424             bottomText.onRender(bottom.el, null);
33425             
33426             item.topEl = top;
33427             item.bottomEl = bottom;
33428         }
33429         
33430         this.barItems.push(item);
33431         
33432         return item;
33433     },
33434     
33435     getActive : function()
33436     {
33437         var active = false;
33438         
33439         Roo.each(this.barItems, function(v){
33440             
33441             if (!v.isActive()) {
33442                 return;
33443             }
33444             
33445             active = v;
33446             return false;
33447             
33448         });
33449         
33450         return active;
33451     },
33452     
33453     setActiveItem : function(item)
33454     {
33455         var prev = false;
33456         
33457         Roo.each(this.barItems, function(v){
33458             if (v.rid == item.rid) {
33459                 return ;
33460             }
33461             
33462             if (v.isActive()) {
33463                 v.setActive(false);
33464                 prev = v;
33465             }
33466         });
33467
33468         item.setActive(true);
33469         
33470         this.fireEvent('changed', this, item, prev);
33471     },
33472     
33473     getBarItem: function(rid)
33474     {
33475         var ret = false;
33476         
33477         Roo.each(this.barItems, function(e) {
33478             if (e.rid != rid) {
33479                 return;
33480             }
33481             
33482             ret =  e;
33483             return false;
33484         });
33485         
33486         return ret;
33487     },
33488     
33489     indexOfItem : function(item)
33490     {
33491         var index = false;
33492         
33493         Roo.each(this.barItems, function(v, i){
33494             
33495             if (v.rid != item.rid) {
33496                 return;
33497             }
33498             
33499             index = i;
33500             return false
33501         });
33502         
33503         return index;
33504     },
33505     
33506     setActiveNext : function()
33507     {
33508         var i = this.indexOfItem(this.getActive());
33509         
33510         if (i > this.barItems.length) {
33511             return;
33512         }
33513         
33514         this.setActiveItem(this.barItems[i+1]);
33515     },
33516     
33517     setActivePrev : function()
33518     {
33519         var i = this.indexOfItem(this.getActive());
33520         
33521         if (i  < 1) {
33522             return;
33523         }
33524         
33525         this.setActiveItem(this.barItems[i-1]);
33526     },
33527     
33528     format : function()
33529     {
33530         if(!this.barItems.length){
33531             return;
33532         }
33533      
33534         var width = 100 / this.barItems.length;
33535         
33536         Roo.each(this.barItems, function(i){
33537             i.el.setStyle('width', width + '%');
33538             i.topEl.el.setStyle('width', width + '%');
33539             i.bottomEl.el.setStyle('width', width + '%');
33540         }, this);
33541         
33542     }
33543     
33544 });
33545 /*
33546  * - LGPL
33547  *
33548  * Nav Progress Item
33549  * 
33550  */
33551
33552 /**
33553  * @class Roo.bootstrap.NavProgressItem
33554  * @extends Roo.bootstrap.Component
33555  * Bootstrap NavProgressItem class
33556  * @cfg {String} rid the reference id
33557  * @cfg {Boolean} active (true|false) Is item active default false
33558  * @cfg {Boolean} disabled (true|false) Is item active default false
33559  * @cfg {String} html
33560  * @cfg {String} position (top|bottom) text position default bottom
33561  * @cfg {String} icon show icon instead of number
33562  * 
33563  * @constructor
33564  * Create a new NavProgressItem
33565  * @param {Object} config The config object
33566  */
33567 Roo.bootstrap.NavProgressItem = function(config){
33568     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33569     this.addEvents({
33570         // raw events
33571         /**
33572          * @event click
33573          * The raw click event for the entire grid.
33574          * @param {Roo.bootstrap.NavProgressItem} this
33575          * @param {Roo.EventObject} e
33576          */
33577         "click" : true
33578     });
33579    
33580 };
33581
33582 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33583     
33584     rid : '',
33585     active : false,
33586     disabled : false,
33587     html : '',
33588     position : 'bottom',
33589     icon : false,
33590     
33591     getAutoCreate : function()
33592     {
33593         var iconCls = 'roo-navigation-bar-item-icon';
33594         
33595         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33596         
33597         var cfg = {
33598             tag: 'li',
33599             cls: 'roo-navigation-bar-item',
33600             cn : [
33601                 {
33602                     tag : 'i',
33603                     cls : iconCls
33604                 }
33605             ]
33606         };
33607         
33608         if(this.active){
33609             cfg.cls += ' active';
33610         }
33611         if(this.disabled){
33612             cfg.cls += ' disabled';
33613         }
33614         
33615         return cfg;
33616     },
33617     
33618     disable : function()
33619     {
33620         this.setDisabled(true);
33621     },
33622     
33623     enable : function()
33624     {
33625         this.setDisabled(false);
33626     },
33627     
33628     initEvents: function() 
33629     {
33630         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33631         
33632         this.iconEl.on('click', this.onClick, this);
33633     },
33634     
33635     onClick : function(e)
33636     {
33637         e.preventDefault();
33638         
33639         if(this.disabled){
33640             return;
33641         }
33642         
33643         if(this.fireEvent('click', this, e) === false){
33644             return;
33645         };
33646         
33647         this.parent().setActiveItem(this);
33648     },
33649     
33650     isActive: function () 
33651     {
33652         return this.active;
33653     },
33654     
33655     setActive : function(state)
33656     {
33657         if(this.active == state){
33658             return;
33659         }
33660         
33661         this.active = state;
33662         
33663         if (state) {
33664             this.el.addClass('active');
33665             return;
33666         }
33667         
33668         this.el.removeClass('active');
33669         
33670         return;
33671     },
33672     
33673     setDisabled : function(state)
33674     {
33675         if(this.disabled == state){
33676             return;
33677         }
33678         
33679         this.disabled = state;
33680         
33681         if (state) {
33682             this.el.addClass('disabled');
33683             return;
33684         }
33685         
33686         this.el.removeClass('disabled');
33687     },
33688     
33689     tooltipEl : function()
33690     {
33691         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33692     }
33693 });
33694  
33695
33696  /*
33697  * - LGPL
33698  *
33699  * FieldLabel
33700  * 
33701  */
33702
33703 /**
33704  * @class Roo.bootstrap.FieldLabel
33705  * @extends Roo.bootstrap.Component
33706  * Bootstrap FieldLabel class
33707  * @cfg {String} html contents of the element
33708  * @cfg {String} tag tag of the element default label
33709  * @cfg {String} cls class of the element
33710  * @cfg {String} target label target 
33711  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33712  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33713  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33714  * @cfg {String} iconTooltip default "This field is required"
33715  * @cfg {String} indicatorpos (left|right) default left
33716  * 
33717  * @constructor
33718  * Create a new FieldLabel
33719  * @param {Object} config The config object
33720  */
33721
33722 Roo.bootstrap.FieldLabel = function(config){
33723     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33724     
33725     this.addEvents({
33726             /**
33727              * @event invalid
33728              * Fires after the field has been marked as invalid.
33729              * @param {Roo.form.FieldLabel} this
33730              * @param {String} msg The validation message
33731              */
33732             invalid : true,
33733             /**
33734              * @event valid
33735              * Fires after the field has been validated with no errors.
33736              * @param {Roo.form.FieldLabel} this
33737              */
33738             valid : true
33739         });
33740 };
33741
33742 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33743     
33744     tag: 'label',
33745     cls: '',
33746     html: '',
33747     target: '',
33748     allowBlank : true,
33749     invalidClass : 'has-warning',
33750     validClass : 'has-success',
33751     iconTooltip : 'This field is required',
33752     indicatorpos : 'left',
33753     
33754     getAutoCreate : function(){
33755         
33756         var cls = "";
33757         if (!this.allowBlank) {
33758             cls  = "visible";
33759         }
33760         
33761         var cfg = {
33762             tag : this.tag,
33763             cls : 'roo-bootstrap-field-label ' + this.cls,
33764             for : this.target,
33765             cn : [
33766                 {
33767                     tag : 'i',
33768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33769                     tooltip : this.iconTooltip
33770                 },
33771                 {
33772                     tag : 'span',
33773                     html : this.html
33774                 }
33775             ] 
33776         };
33777         
33778         if(this.indicatorpos == 'right'){
33779             var cfg = {
33780                 tag : this.tag,
33781                 cls : 'roo-bootstrap-field-label ' + this.cls,
33782                 for : this.target,
33783                 cn : [
33784                     {
33785                         tag : 'span',
33786                         html : this.html
33787                     },
33788                     {
33789                         tag : 'i',
33790                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33791                         tooltip : this.iconTooltip
33792                     }
33793                 ] 
33794             };
33795         }
33796         
33797         return cfg;
33798     },
33799     
33800     initEvents: function() 
33801     {
33802         Roo.bootstrap.Element.superclass.initEvents.call(this);
33803         
33804         this.indicator = this.indicatorEl();
33805         
33806         if(this.indicator){
33807             this.indicator.removeClass('visible');
33808             this.indicator.addClass('invisible');
33809         }
33810         
33811         Roo.bootstrap.FieldLabel.register(this);
33812     },
33813     
33814     indicatorEl : function()
33815     {
33816         var indicator = this.el.select('i.roo-required-indicator',true).first();
33817         
33818         if(!indicator){
33819             return false;
33820         }
33821         
33822         return indicator;
33823         
33824     },
33825     
33826     /**
33827      * Mark this field as valid
33828      */
33829     markValid : function()
33830     {
33831         if(this.indicator){
33832             this.indicator.removeClass('visible');
33833             this.indicator.addClass('invisible');
33834         }
33835         if (Roo.bootstrap.version == 3) {
33836             this.el.removeClass(this.invalidClass);
33837             this.el.addClass(this.validClass);
33838         } else {
33839             this.el.removeClass('is-invalid');
33840             this.el.addClass('is-valid');
33841         }
33842         
33843         
33844         this.fireEvent('valid', this);
33845     },
33846     
33847     /**
33848      * Mark this field as invalid
33849      * @param {String} msg The validation message
33850      */
33851     markInvalid : function(msg)
33852     {
33853         if(this.indicator){
33854             this.indicator.removeClass('invisible');
33855             this.indicator.addClass('visible');
33856         }
33857           if (Roo.bootstrap.version == 3) {
33858             this.el.removeClass(this.validClass);
33859             this.el.addClass(this.invalidClass);
33860         } else {
33861             this.el.removeClass('is-valid');
33862             this.el.addClass('is-invalid');
33863         }
33864         
33865         
33866         this.fireEvent('invalid', this, msg);
33867     }
33868     
33869    
33870 });
33871
33872 Roo.apply(Roo.bootstrap.FieldLabel, {
33873     
33874     groups: {},
33875     
33876      /**
33877     * register a FieldLabel Group
33878     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33879     */
33880     register : function(label)
33881     {
33882         if(this.groups.hasOwnProperty(label.target)){
33883             return;
33884         }
33885      
33886         this.groups[label.target] = label;
33887         
33888     },
33889     /**
33890     * fetch a FieldLabel Group based on the target
33891     * @param {string} target
33892     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33893     */
33894     get: function(target) {
33895         if (typeof(this.groups[target]) == 'undefined') {
33896             return false;
33897         }
33898         
33899         return this.groups[target] ;
33900     }
33901 });
33902
33903  
33904
33905  /*
33906  * - LGPL
33907  *
33908  * page DateSplitField.
33909  * 
33910  */
33911
33912
33913 /**
33914  * @class Roo.bootstrap.DateSplitField
33915  * @extends Roo.bootstrap.Component
33916  * Bootstrap DateSplitField class
33917  * @cfg {string} fieldLabel - the label associated
33918  * @cfg {Number} labelWidth set the width of label (0-12)
33919  * @cfg {String} labelAlign (top|left)
33920  * @cfg {Boolean} dayAllowBlank (true|false) default false
33921  * @cfg {Boolean} monthAllowBlank (true|false) default false
33922  * @cfg {Boolean} yearAllowBlank (true|false) default false
33923  * @cfg {string} dayPlaceholder 
33924  * @cfg {string} monthPlaceholder
33925  * @cfg {string} yearPlaceholder
33926  * @cfg {string} dayFormat default 'd'
33927  * @cfg {string} monthFormat default 'm'
33928  * @cfg {string} yearFormat default 'Y'
33929  * @cfg {Number} labellg set the width of label (1-12)
33930  * @cfg {Number} labelmd set the width of label (1-12)
33931  * @cfg {Number} labelsm set the width of label (1-12)
33932  * @cfg {Number} labelxs set the width of label (1-12)
33933
33934  *     
33935  * @constructor
33936  * Create a new DateSplitField
33937  * @param {Object} config The config object
33938  */
33939
33940 Roo.bootstrap.DateSplitField = function(config){
33941     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33942     
33943     this.addEvents({
33944         // raw events
33945          /**
33946          * @event years
33947          * getting the data of years
33948          * @param {Roo.bootstrap.DateSplitField} this
33949          * @param {Object} years
33950          */
33951         "years" : true,
33952         /**
33953          * @event days
33954          * getting the data of days
33955          * @param {Roo.bootstrap.DateSplitField} this
33956          * @param {Object} days
33957          */
33958         "days" : true,
33959         /**
33960          * @event invalid
33961          * Fires after the field has been marked as invalid.
33962          * @param {Roo.form.Field} this
33963          * @param {String} msg The validation message
33964          */
33965         invalid : true,
33966        /**
33967          * @event valid
33968          * Fires after the field has been validated with no errors.
33969          * @param {Roo.form.Field} this
33970          */
33971         valid : true
33972     });
33973 };
33974
33975 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33976     
33977     fieldLabel : '',
33978     labelAlign : 'top',
33979     labelWidth : 3,
33980     dayAllowBlank : false,
33981     monthAllowBlank : false,
33982     yearAllowBlank : false,
33983     dayPlaceholder : '',
33984     monthPlaceholder : '',
33985     yearPlaceholder : '',
33986     dayFormat : 'd',
33987     monthFormat : 'm',
33988     yearFormat : 'Y',
33989     isFormField : true,
33990     labellg : 0,
33991     labelmd : 0,
33992     labelsm : 0,
33993     labelxs : 0,
33994     
33995     getAutoCreate : function()
33996     {
33997         var cfg = {
33998             tag : 'div',
33999             cls : 'row roo-date-split-field-group',
34000             cn : [
34001                 {
34002                     tag : 'input',
34003                     type : 'hidden',
34004                     cls : 'form-hidden-field roo-date-split-field-group-value',
34005                     name : this.name
34006                 }
34007             ]
34008         };
34009         
34010         var labelCls = 'col-md-12';
34011         var contentCls = 'col-md-4';
34012         
34013         if(this.fieldLabel){
34014             
34015             var label = {
34016                 tag : 'div',
34017                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34018                 cn : [
34019                     {
34020                         tag : 'label',
34021                         html : this.fieldLabel
34022                     }
34023                 ]
34024             };
34025             
34026             if(this.labelAlign == 'left'){
34027             
34028                 if(this.labelWidth > 12){
34029                     label.style = "width: " + this.labelWidth + 'px';
34030                 }
34031
34032                 if(this.labelWidth < 13 && this.labelmd == 0){
34033                     this.labelmd = this.labelWidth;
34034                 }
34035
34036                 if(this.labellg > 0){
34037                     labelCls = ' col-lg-' + this.labellg;
34038                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34039                 }
34040
34041                 if(this.labelmd > 0){
34042                     labelCls = ' col-md-' + this.labelmd;
34043                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34044                 }
34045
34046                 if(this.labelsm > 0){
34047                     labelCls = ' col-sm-' + this.labelsm;
34048                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34049                 }
34050
34051                 if(this.labelxs > 0){
34052                     labelCls = ' col-xs-' + this.labelxs;
34053                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34054                 }
34055             }
34056             
34057             label.cls += ' ' + labelCls;
34058             
34059             cfg.cn.push(label);
34060         }
34061         
34062         Roo.each(['day', 'month', 'year'], function(t){
34063             cfg.cn.push({
34064                 tag : 'div',
34065                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34066             });
34067         }, this);
34068         
34069         return cfg;
34070     },
34071     
34072     inputEl: function ()
34073     {
34074         return this.el.select('.roo-date-split-field-group-value', true).first();
34075     },
34076     
34077     onRender : function(ct, position) 
34078     {
34079         var _this = this;
34080         
34081         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34082         
34083         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34084         
34085         this.dayField = new Roo.bootstrap.ComboBox({
34086             allowBlank : this.dayAllowBlank,
34087             alwaysQuery : true,
34088             displayField : 'value',
34089             editable : false,
34090             fieldLabel : '',
34091             forceSelection : true,
34092             mode : 'local',
34093             placeholder : this.dayPlaceholder,
34094             selectOnFocus : true,
34095             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34096             triggerAction : 'all',
34097             typeAhead : true,
34098             valueField : 'value',
34099             store : new Roo.data.SimpleStore({
34100                 data : (function() {    
34101                     var days = [];
34102                     _this.fireEvent('days', _this, days);
34103                     return days;
34104                 })(),
34105                 fields : [ 'value' ]
34106             }),
34107             listeners : {
34108                 select : function (_self, record, index)
34109                 {
34110                     _this.setValue(_this.getValue());
34111                 }
34112             }
34113         });
34114
34115         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34116         
34117         this.monthField = new Roo.bootstrap.MonthField({
34118             after : '<i class=\"fa fa-calendar\"></i>',
34119             allowBlank : this.monthAllowBlank,
34120             placeholder : this.monthPlaceholder,
34121             readOnly : true,
34122             listeners : {
34123                 render : function (_self)
34124                 {
34125                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34126                         e.preventDefault();
34127                         _self.focus();
34128                     });
34129                 },
34130                 select : function (_self, oldvalue, newvalue)
34131                 {
34132                     _this.setValue(_this.getValue());
34133                 }
34134             }
34135         });
34136         
34137         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34138         
34139         this.yearField = new Roo.bootstrap.ComboBox({
34140             allowBlank : this.yearAllowBlank,
34141             alwaysQuery : true,
34142             displayField : 'value',
34143             editable : false,
34144             fieldLabel : '',
34145             forceSelection : true,
34146             mode : 'local',
34147             placeholder : this.yearPlaceholder,
34148             selectOnFocus : true,
34149             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34150             triggerAction : 'all',
34151             typeAhead : true,
34152             valueField : 'value',
34153             store : new Roo.data.SimpleStore({
34154                 data : (function() {
34155                     var years = [];
34156                     _this.fireEvent('years', _this, years);
34157                     return years;
34158                 })(),
34159                 fields : [ 'value' ]
34160             }),
34161             listeners : {
34162                 select : function (_self, record, index)
34163                 {
34164                     _this.setValue(_this.getValue());
34165                 }
34166             }
34167         });
34168
34169         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34170     },
34171     
34172     setValue : function(v, format)
34173     {
34174         this.inputEl.dom.value = v;
34175         
34176         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34177         
34178         var d = Date.parseDate(v, f);
34179         
34180         if(!d){
34181             this.validate();
34182             return;
34183         }
34184         
34185         this.setDay(d.format(this.dayFormat));
34186         this.setMonth(d.format(this.monthFormat));
34187         this.setYear(d.format(this.yearFormat));
34188         
34189         this.validate();
34190         
34191         return;
34192     },
34193     
34194     setDay : function(v)
34195     {
34196         this.dayField.setValue(v);
34197         this.inputEl.dom.value = this.getValue();
34198         this.validate();
34199         return;
34200     },
34201     
34202     setMonth : function(v)
34203     {
34204         this.monthField.setValue(v, true);
34205         this.inputEl.dom.value = this.getValue();
34206         this.validate();
34207         return;
34208     },
34209     
34210     setYear : function(v)
34211     {
34212         this.yearField.setValue(v);
34213         this.inputEl.dom.value = this.getValue();
34214         this.validate();
34215         return;
34216     },
34217     
34218     getDay : function()
34219     {
34220         return this.dayField.getValue();
34221     },
34222     
34223     getMonth : function()
34224     {
34225         return this.monthField.getValue();
34226     },
34227     
34228     getYear : function()
34229     {
34230         return this.yearField.getValue();
34231     },
34232     
34233     getValue : function()
34234     {
34235         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34236         
34237         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34238         
34239         return date;
34240     },
34241     
34242     reset : function()
34243     {
34244         this.setDay('');
34245         this.setMonth('');
34246         this.setYear('');
34247         this.inputEl.dom.value = '';
34248         this.validate();
34249         return;
34250     },
34251     
34252     validate : function()
34253     {
34254         var d = this.dayField.validate();
34255         var m = this.monthField.validate();
34256         var y = this.yearField.validate();
34257         
34258         var valid = true;
34259         
34260         if(
34261                 (!this.dayAllowBlank && !d) ||
34262                 (!this.monthAllowBlank && !m) ||
34263                 (!this.yearAllowBlank && !y)
34264         ){
34265             valid = false;
34266         }
34267         
34268         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34269             return valid;
34270         }
34271         
34272         if(valid){
34273             this.markValid();
34274             return valid;
34275         }
34276         
34277         this.markInvalid();
34278         
34279         return valid;
34280     },
34281     
34282     markValid : function()
34283     {
34284         
34285         var label = this.el.select('label', true).first();
34286         var icon = this.el.select('i.fa-star', true).first();
34287
34288         if(label && icon){
34289             icon.remove();
34290         }
34291         
34292         this.fireEvent('valid', this);
34293     },
34294     
34295      /**
34296      * Mark this field as invalid
34297      * @param {String} msg The validation message
34298      */
34299     markInvalid : function(msg)
34300     {
34301         
34302         var label = this.el.select('label', true).first();
34303         var icon = this.el.select('i.fa-star', true).first();
34304
34305         if(label && !icon){
34306             this.el.select('.roo-date-split-field-label', true).createChild({
34307                 tag : 'i',
34308                 cls : 'text-danger fa fa-lg fa-star',
34309                 tooltip : 'This field is required',
34310                 style : 'margin-right:5px;'
34311             }, label, true);
34312         }
34313         
34314         this.fireEvent('invalid', this, msg);
34315     },
34316     
34317     clearInvalid : function()
34318     {
34319         var label = this.el.select('label', true).first();
34320         var icon = this.el.select('i.fa-star', true).first();
34321
34322         if(label && icon){
34323             icon.remove();
34324         }
34325         
34326         this.fireEvent('valid', this);
34327     },
34328     
34329     getName: function()
34330     {
34331         return this.name;
34332     }
34333     
34334 });
34335
34336  /**
34337  *
34338  * This is based on 
34339  * http://masonry.desandro.com
34340  *
34341  * The idea is to render all the bricks based on vertical width...
34342  *
34343  * The original code extends 'outlayer' - we might need to use that....
34344  * 
34345  */
34346
34347
34348 /**
34349  * @class Roo.bootstrap.LayoutMasonry
34350  * @extends Roo.bootstrap.Component
34351  * Bootstrap Layout Masonry class
34352  * 
34353  * @constructor
34354  * Create a new Element
34355  * @param {Object} config The config object
34356  */
34357
34358 Roo.bootstrap.LayoutMasonry = function(config){
34359     
34360     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34361     
34362     this.bricks = [];
34363     
34364     Roo.bootstrap.LayoutMasonry.register(this);
34365     
34366     this.addEvents({
34367         // raw events
34368         /**
34369          * @event layout
34370          * Fire after layout the items
34371          * @param {Roo.bootstrap.LayoutMasonry} this
34372          * @param {Roo.EventObject} e
34373          */
34374         "layout" : true
34375     });
34376     
34377 };
34378
34379 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34380     
34381     /**
34382      * @cfg {Boolean} isLayoutInstant = no animation?
34383      */   
34384     isLayoutInstant : false, // needed?
34385    
34386     /**
34387      * @cfg {Number} boxWidth  width of the columns
34388      */   
34389     boxWidth : 450,
34390     
34391       /**
34392      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34393      */   
34394     boxHeight : 0,
34395     
34396     /**
34397      * @cfg {Number} padWidth padding below box..
34398      */   
34399     padWidth : 10, 
34400     
34401     /**
34402      * @cfg {Number} gutter gutter width..
34403      */   
34404     gutter : 10,
34405     
34406      /**
34407      * @cfg {Number} maxCols maximum number of columns
34408      */   
34409     
34410     maxCols: 0,
34411     
34412     /**
34413      * @cfg {Boolean} isAutoInitial defalut true
34414      */   
34415     isAutoInitial : true, 
34416     
34417     containerWidth: 0,
34418     
34419     /**
34420      * @cfg {Boolean} isHorizontal defalut false
34421      */   
34422     isHorizontal : false, 
34423
34424     currentSize : null,
34425     
34426     tag: 'div',
34427     
34428     cls: '',
34429     
34430     bricks: null, //CompositeElement
34431     
34432     cols : 1,
34433     
34434     _isLayoutInited : false,
34435     
34436 //    isAlternative : false, // only use for vertical layout...
34437     
34438     /**
34439      * @cfg {Number} alternativePadWidth padding below box..
34440      */   
34441     alternativePadWidth : 50,
34442     
34443     selectedBrick : [],
34444     
34445     getAutoCreate : function(){
34446         
34447         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34448         
34449         var cfg = {
34450             tag: this.tag,
34451             cls: 'blog-masonary-wrapper ' + this.cls,
34452             cn : {
34453                 cls : 'mas-boxes masonary'
34454             }
34455         };
34456         
34457         return cfg;
34458     },
34459     
34460     getChildContainer: function( )
34461     {
34462         if (this.boxesEl) {
34463             return this.boxesEl;
34464         }
34465         
34466         this.boxesEl = this.el.select('.mas-boxes').first();
34467         
34468         return this.boxesEl;
34469     },
34470     
34471     
34472     initEvents : function()
34473     {
34474         var _this = this;
34475         
34476         if(this.isAutoInitial){
34477             Roo.log('hook children rendered');
34478             this.on('childrenrendered', function() {
34479                 Roo.log('children rendered');
34480                 _this.initial();
34481             } ,this);
34482         }
34483     },
34484     
34485     initial : function()
34486     {
34487         this.selectedBrick = [];
34488         
34489         this.currentSize = this.el.getBox(true);
34490         
34491         Roo.EventManager.onWindowResize(this.resize, this); 
34492
34493         if(!this.isAutoInitial){
34494             this.layout();
34495             return;
34496         }
34497         
34498         this.layout();
34499         
34500         return;
34501         //this.layout.defer(500,this);
34502         
34503     },
34504     
34505     resize : function()
34506     {
34507         var cs = this.el.getBox(true);
34508         
34509         if (
34510                 this.currentSize.width == cs.width && 
34511                 this.currentSize.x == cs.x && 
34512                 this.currentSize.height == cs.height && 
34513                 this.currentSize.y == cs.y 
34514         ) {
34515             Roo.log("no change in with or X or Y");
34516             return;
34517         }
34518         
34519         this.currentSize = cs;
34520         
34521         this.layout();
34522         
34523     },
34524     
34525     layout : function()
34526     {   
34527         this._resetLayout();
34528         
34529         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34530         
34531         this.layoutItems( isInstant );
34532       
34533         this._isLayoutInited = true;
34534         
34535         this.fireEvent('layout', this);
34536         
34537     },
34538     
34539     _resetLayout : function()
34540     {
34541         if(this.isHorizontal){
34542             this.horizontalMeasureColumns();
34543             return;
34544         }
34545         
34546         this.verticalMeasureColumns();
34547         
34548     },
34549     
34550     verticalMeasureColumns : function()
34551     {
34552         this.getContainerWidth();
34553         
34554 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34555 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34556 //            return;
34557 //        }
34558         
34559         var boxWidth = this.boxWidth + this.padWidth;
34560         
34561         if(this.containerWidth < this.boxWidth){
34562             boxWidth = this.containerWidth
34563         }
34564         
34565         var containerWidth = this.containerWidth;
34566         
34567         var cols = Math.floor(containerWidth / boxWidth);
34568         
34569         this.cols = Math.max( cols, 1 );
34570         
34571         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34572         
34573         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34574         
34575         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34576         
34577         this.colWidth = boxWidth + avail - this.padWidth;
34578         
34579         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34580         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34581     },
34582     
34583     horizontalMeasureColumns : function()
34584     {
34585         this.getContainerWidth();
34586         
34587         var boxWidth = this.boxWidth;
34588         
34589         if(this.containerWidth < boxWidth){
34590             boxWidth = this.containerWidth;
34591         }
34592         
34593         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34594         
34595         this.el.setHeight(boxWidth);
34596         
34597     },
34598     
34599     getContainerWidth : function()
34600     {
34601         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34602     },
34603     
34604     layoutItems : function( isInstant )
34605     {
34606         Roo.log(this.bricks);
34607         
34608         var items = Roo.apply([], this.bricks);
34609         
34610         if(this.isHorizontal){
34611             this._horizontalLayoutItems( items , isInstant );
34612             return;
34613         }
34614         
34615 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34616 //            this._verticalAlternativeLayoutItems( items , isInstant );
34617 //            return;
34618 //        }
34619         
34620         this._verticalLayoutItems( items , isInstant );
34621         
34622     },
34623     
34624     _verticalLayoutItems : function ( items , isInstant)
34625     {
34626         if ( !items || !items.length ) {
34627             return;
34628         }
34629         
34630         var standard = [
34631             ['xs', 'xs', 'xs', 'tall'],
34632             ['xs', 'xs', 'tall'],
34633             ['xs', 'xs', 'sm'],
34634             ['xs', 'xs', 'xs'],
34635             ['xs', 'tall'],
34636             ['xs', 'sm'],
34637             ['xs', 'xs'],
34638             ['xs'],
34639             
34640             ['sm', 'xs', 'xs'],
34641             ['sm', 'xs'],
34642             ['sm'],
34643             
34644             ['tall', 'xs', 'xs', 'xs'],
34645             ['tall', 'xs', 'xs'],
34646             ['tall', 'xs'],
34647             ['tall']
34648             
34649         ];
34650         
34651         var queue = [];
34652         
34653         var boxes = [];
34654         
34655         var box = [];
34656         
34657         Roo.each(items, function(item, k){
34658             
34659             switch (item.size) {
34660                 // these layouts take up a full box,
34661                 case 'md' :
34662                 case 'md-left' :
34663                 case 'md-right' :
34664                 case 'wide' :
34665                     
34666                     if(box.length){
34667                         boxes.push(box);
34668                         box = [];
34669                     }
34670                     
34671                     boxes.push([item]);
34672                     
34673                     break;
34674                     
34675                 case 'xs' :
34676                 case 'sm' :
34677                 case 'tall' :
34678                     
34679                     box.push(item);
34680                     
34681                     break;
34682                 default :
34683                     break;
34684                     
34685             }
34686             
34687         }, this);
34688         
34689         if(box.length){
34690             boxes.push(box);
34691             box = [];
34692         }
34693         
34694         var filterPattern = function(box, length)
34695         {
34696             if(!box.length){
34697                 return;
34698             }
34699             
34700             var match = false;
34701             
34702             var pattern = box.slice(0, length);
34703             
34704             var format = [];
34705             
34706             Roo.each(pattern, function(i){
34707                 format.push(i.size);
34708             }, this);
34709             
34710             Roo.each(standard, function(s){
34711                 
34712                 if(String(s) != String(format)){
34713                     return;
34714                 }
34715                 
34716                 match = true;
34717                 return false;
34718                 
34719             }, this);
34720             
34721             if(!match && length == 1){
34722                 return;
34723             }
34724             
34725             if(!match){
34726                 filterPattern(box, length - 1);
34727                 return;
34728             }
34729                 
34730             queue.push(pattern);
34731
34732             box = box.slice(length, box.length);
34733
34734             filterPattern(box, 4);
34735
34736             return;
34737             
34738         }
34739         
34740         Roo.each(boxes, function(box, k){
34741             
34742             if(!box.length){
34743                 return;
34744             }
34745             
34746             if(box.length == 1){
34747                 queue.push(box);
34748                 return;
34749             }
34750             
34751             filterPattern(box, 4);
34752             
34753         }, this);
34754         
34755         this._processVerticalLayoutQueue( queue, isInstant );
34756         
34757     },
34758     
34759 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34760 //    {
34761 //        if ( !items || !items.length ) {
34762 //            return;
34763 //        }
34764 //
34765 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34766 //        
34767 //    },
34768     
34769     _horizontalLayoutItems : function ( items , isInstant)
34770     {
34771         if ( !items || !items.length || items.length < 3) {
34772             return;
34773         }
34774         
34775         items.reverse();
34776         
34777         var eItems = items.slice(0, 3);
34778         
34779         items = items.slice(3, items.length);
34780         
34781         var standard = [
34782             ['xs', 'xs', 'xs', 'wide'],
34783             ['xs', 'xs', 'wide'],
34784             ['xs', 'xs', 'sm'],
34785             ['xs', 'xs', 'xs'],
34786             ['xs', 'wide'],
34787             ['xs', 'sm'],
34788             ['xs', 'xs'],
34789             ['xs'],
34790             
34791             ['sm', 'xs', 'xs'],
34792             ['sm', 'xs'],
34793             ['sm'],
34794             
34795             ['wide', 'xs', 'xs', 'xs'],
34796             ['wide', 'xs', 'xs'],
34797             ['wide', 'xs'],
34798             ['wide'],
34799             
34800             ['wide-thin']
34801         ];
34802         
34803         var queue = [];
34804         
34805         var boxes = [];
34806         
34807         var box = [];
34808         
34809         Roo.each(items, function(item, k){
34810             
34811             switch (item.size) {
34812                 case 'md' :
34813                 case 'md-left' :
34814                 case 'md-right' :
34815                 case 'tall' :
34816                     
34817                     if(box.length){
34818                         boxes.push(box);
34819                         box = [];
34820                     }
34821                     
34822                     boxes.push([item]);
34823                     
34824                     break;
34825                     
34826                 case 'xs' :
34827                 case 'sm' :
34828                 case 'wide' :
34829                 case 'wide-thin' :
34830                     
34831                     box.push(item);
34832                     
34833                     break;
34834                 default :
34835                     break;
34836                     
34837             }
34838             
34839         }, this);
34840         
34841         if(box.length){
34842             boxes.push(box);
34843             box = [];
34844         }
34845         
34846         var filterPattern = function(box, length)
34847         {
34848             if(!box.length){
34849                 return;
34850             }
34851             
34852             var match = false;
34853             
34854             var pattern = box.slice(0, length);
34855             
34856             var format = [];
34857             
34858             Roo.each(pattern, function(i){
34859                 format.push(i.size);
34860             }, this);
34861             
34862             Roo.each(standard, function(s){
34863                 
34864                 if(String(s) != String(format)){
34865                     return;
34866                 }
34867                 
34868                 match = true;
34869                 return false;
34870                 
34871             }, this);
34872             
34873             if(!match && length == 1){
34874                 return;
34875             }
34876             
34877             if(!match){
34878                 filterPattern(box, length - 1);
34879                 return;
34880             }
34881                 
34882             queue.push(pattern);
34883
34884             box = box.slice(length, box.length);
34885
34886             filterPattern(box, 4);
34887
34888             return;
34889             
34890         }
34891         
34892         Roo.each(boxes, function(box, k){
34893             
34894             if(!box.length){
34895                 return;
34896             }
34897             
34898             if(box.length == 1){
34899                 queue.push(box);
34900                 return;
34901             }
34902             
34903             filterPattern(box, 4);
34904             
34905         }, this);
34906         
34907         
34908         var prune = [];
34909         
34910         var pos = this.el.getBox(true);
34911         
34912         var minX = pos.x;
34913         
34914         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34915         
34916         var hit_end = false;
34917         
34918         Roo.each(queue, function(box){
34919             
34920             if(hit_end){
34921                 
34922                 Roo.each(box, function(b){
34923                 
34924                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34925                     b.el.hide();
34926
34927                 }, this);
34928
34929                 return;
34930             }
34931             
34932             var mx = 0;
34933             
34934             Roo.each(box, function(b){
34935                 
34936                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34937                 b.el.show();
34938
34939                 mx = Math.max(mx, b.x);
34940                 
34941             }, this);
34942             
34943             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34944             
34945             if(maxX < minX){
34946                 
34947                 Roo.each(box, function(b){
34948                 
34949                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34950                     b.el.hide();
34951                     
34952                 }, this);
34953                 
34954                 hit_end = true;
34955                 
34956                 return;
34957             }
34958             
34959             prune.push(box);
34960             
34961         }, this);
34962         
34963         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34964     },
34965     
34966     /** Sets position of item in DOM
34967     * @param {Element} item
34968     * @param {Number} x - horizontal position
34969     * @param {Number} y - vertical position
34970     * @param {Boolean} isInstant - disables transitions
34971     */
34972     _processVerticalLayoutQueue : function( queue, isInstant )
34973     {
34974         var pos = this.el.getBox(true);
34975         var x = pos.x;
34976         var y = pos.y;
34977         var maxY = [];
34978         
34979         for (var i = 0; i < this.cols; i++){
34980             maxY[i] = pos.y;
34981         }
34982         
34983         Roo.each(queue, function(box, k){
34984             
34985             var col = k % this.cols;
34986             
34987             Roo.each(box, function(b,kk){
34988                 
34989                 b.el.position('absolute');
34990                 
34991                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34992                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34993                 
34994                 if(b.size == 'md-left' || b.size == 'md-right'){
34995                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34996                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34997                 }
34998                 
34999                 b.el.setWidth(width);
35000                 b.el.setHeight(height);
35001                 // iframe?
35002                 b.el.select('iframe',true).setSize(width,height);
35003                 
35004             }, this);
35005             
35006             for (var i = 0; i < this.cols; i++){
35007                 
35008                 if(maxY[i] < maxY[col]){
35009                     col = i;
35010                     continue;
35011                 }
35012                 
35013                 col = Math.min(col, i);
35014                 
35015             }
35016             
35017             x = pos.x + col * (this.colWidth + this.padWidth);
35018             
35019             y = maxY[col];
35020             
35021             var positions = [];
35022             
35023             switch (box.length){
35024                 case 1 :
35025                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35026                     break;
35027                 case 2 :
35028                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35029                     break;
35030                 case 3 :
35031                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35032                     break;
35033                 case 4 :
35034                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35035                     break;
35036                 default :
35037                     break;
35038             }
35039             
35040             Roo.each(box, function(b,kk){
35041                 
35042                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35043                 
35044                 var sz = b.el.getSize();
35045                 
35046                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35047                 
35048             }, this);
35049             
35050         }, this);
35051         
35052         var mY = 0;
35053         
35054         for (var i = 0; i < this.cols; i++){
35055             mY = Math.max(mY, maxY[i]);
35056         }
35057         
35058         this.el.setHeight(mY - pos.y);
35059         
35060     },
35061     
35062 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35063 //    {
35064 //        var pos = this.el.getBox(true);
35065 //        var x = pos.x;
35066 //        var y = pos.y;
35067 //        var maxX = pos.right;
35068 //        
35069 //        var maxHeight = 0;
35070 //        
35071 //        Roo.each(items, function(item, k){
35072 //            
35073 //            var c = k % 2;
35074 //            
35075 //            item.el.position('absolute');
35076 //                
35077 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35078 //
35079 //            item.el.setWidth(width);
35080 //
35081 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35082 //
35083 //            item.el.setHeight(height);
35084 //            
35085 //            if(c == 0){
35086 //                item.el.setXY([x, y], isInstant ? false : true);
35087 //            } else {
35088 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35089 //            }
35090 //            
35091 //            y = y + height + this.alternativePadWidth;
35092 //            
35093 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35094 //            
35095 //        }, this);
35096 //        
35097 //        this.el.setHeight(maxHeight);
35098 //        
35099 //    },
35100     
35101     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35102     {
35103         var pos = this.el.getBox(true);
35104         
35105         var minX = pos.x;
35106         var minY = pos.y;
35107         
35108         var maxX = pos.right;
35109         
35110         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35111         
35112         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35113         
35114         Roo.each(queue, function(box, k){
35115             
35116             Roo.each(box, function(b, kk){
35117                 
35118                 b.el.position('absolute');
35119                 
35120                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35121                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35122                 
35123                 if(b.size == 'md-left' || b.size == 'md-right'){
35124                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35125                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35126                 }
35127                 
35128                 b.el.setWidth(width);
35129                 b.el.setHeight(height);
35130                 
35131             }, this);
35132             
35133             if(!box.length){
35134                 return;
35135             }
35136             
35137             var positions = [];
35138             
35139             switch (box.length){
35140                 case 1 :
35141                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35142                     break;
35143                 case 2 :
35144                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35145                     break;
35146                 case 3 :
35147                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35148                     break;
35149                 case 4 :
35150                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35151                     break;
35152                 default :
35153                     break;
35154             }
35155             
35156             Roo.each(box, function(b,kk){
35157                 
35158                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35159                 
35160                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35161                 
35162             }, this);
35163             
35164         }, this);
35165         
35166     },
35167     
35168     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35169     {
35170         Roo.each(eItems, function(b,k){
35171             
35172             b.size = (k == 0) ? 'sm' : 'xs';
35173             b.x = (k == 0) ? 2 : 1;
35174             b.y = (k == 0) ? 2 : 1;
35175             
35176             b.el.position('absolute');
35177             
35178             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35179                 
35180             b.el.setWidth(width);
35181             
35182             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35183             
35184             b.el.setHeight(height);
35185             
35186         }, this);
35187
35188         var positions = [];
35189         
35190         positions.push({
35191             x : maxX - this.unitWidth * 2 - this.gutter,
35192             y : minY
35193         });
35194         
35195         positions.push({
35196             x : maxX - this.unitWidth,
35197             y : minY + (this.unitWidth + this.gutter) * 2
35198         });
35199         
35200         positions.push({
35201             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35202             y : minY
35203         });
35204         
35205         Roo.each(eItems, function(b,k){
35206             
35207             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35208
35209         }, this);
35210         
35211     },
35212     
35213     getVerticalOneBoxColPositions : function(x, y, box)
35214     {
35215         var pos = [];
35216         
35217         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35218         
35219         if(box[0].size == 'md-left'){
35220             rand = 0;
35221         }
35222         
35223         if(box[0].size == 'md-right'){
35224             rand = 1;
35225         }
35226         
35227         pos.push({
35228             x : x + (this.unitWidth + this.gutter) * rand,
35229             y : y
35230         });
35231         
35232         return pos;
35233     },
35234     
35235     getVerticalTwoBoxColPositions : function(x, y, box)
35236     {
35237         var pos = [];
35238         
35239         if(box[0].size == 'xs'){
35240             
35241             pos.push({
35242                 x : x,
35243                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35244             });
35245
35246             pos.push({
35247                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35248                 y : y
35249             });
35250             
35251             return pos;
35252             
35253         }
35254         
35255         pos.push({
35256             x : x,
35257             y : y
35258         });
35259
35260         pos.push({
35261             x : x + (this.unitWidth + this.gutter) * 2,
35262             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35263         });
35264         
35265         return pos;
35266         
35267     },
35268     
35269     getVerticalThreeBoxColPositions : function(x, y, box)
35270     {
35271         var pos = [];
35272         
35273         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35274             
35275             pos.push({
35276                 x : x,
35277                 y : y
35278             });
35279
35280             pos.push({
35281                 x : x + (this.unitWidth + this.gutter) * 1,
35282                 y : y
35283             });
35284             
35285             pos.push({
35286                 x : x + (this.unitWidth + this.gutter) * 2,
35287                 y : y
35288             });
35289             
35290             return pos;
35291             
35292         }
35293         
35294         if(box[0].size == 'xs' && box[1].size == 'xs'){
35295             
35296             pos.push({
35297                 x : x,
35298                 y : y
35299             });
35300
35301             pos.push({
35302                 x : x,
35303                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35304             });
35305             
35306             pos.push({
35307                 x : x + (this.unitWidth + this.gutter) * 1,
35308                 y : y
35309             });
35310             
35311             return pos;
35312             
35313         }
35314         
35315         pos.push({
35316             x : x,
35317             y : y
35318         });
35319
35320         pos.push({
35321             x : x + (this.unitWidth + this.gutter) * 2,
35322             y : y
35323         });
35324
35325         pos.push({
35326             x : x + (this.unitWidth + this.gutter) * 2,
35327             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35328         });
35329             
35330         return pos;
35331         
35332     },
35333     
35334     getVerticalFourBoxColPositions : function(x, y, box)
35335     {
35336         var pos = [];
35337         
35338         if(box[0].size == 'xs'){
35339             
35340             pos.push({
35341                 x : x,
35342                 y : y
35343             });
35344
35345             pos.push({
35346                 x : x,
35347                 y : y + (this.unitHeight + this.gutter) * 1
35348             });
35349             
35350             pos.push({
35351                 x : x,
35352                 y : y + (this.unitHeight + this.gutter) * 2
35353             });
35354             
35355             pos.push({
35356                 x : x + (this.unitWidth + this.gutter) * 1,
35357                 y : y
35358             });
35359             
35360             return pos;
35361             
35362         }
35363         
35364         pos.push({
35365             x : x,
35366             y : y
35367         });
35368
35369         pos.push({
35370             x : x + (this.unitWidth + this.gutter) * 2,
35371             y : y
35372         });
35373
35374         pos.push({
35375             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35376             y : y + (this.unitHeight + this.gutter) * 1
35377         });
35378
35379         pos.push({
35380             x : x + (this.unitWidth + this.gutter) * 2,
35381             y : y + (this.unitWidth + this.gutter) * 2
35382         });
35383
35384         return pos;
35385         
35386     },
35387     
35388     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35389     {
35390         var pos = [];
35391         
35392         if(box[0].size == 'md-left'){
35393             pos.push({
35394                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35395                 y : minY
35396             });
35397             
35398             return pos;
35399         }
35400         
35401         if(box[0].size == 'md-right'){
35402             pos.push({
35403                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35404                 y : minY + (this.unitWidth + this.gutter) * 1
35405             });
35406             
35407             return pos;
35408         }
35409         
35410         var rand = Math.floor(Math.random() * (4 - box[0].y));
35411         
35412         pos.push({
35413             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35414             y : minY + (this.unitWidth + this.gutter) * rand
35415         });
35416         
35417         return pos;
35418         
35419     },
35420     
35421     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35422     {
35423         var pos = [];
35424         
35425         if(box[0].size == 'xs'){
35426             
35427             pos.push({
35428                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35429                 y : minY
35430             });
35431
35432             pos.push({
35433                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35434                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35435             });
35436             
35437             return pos;
35438             
35439         }
35440         
35441         pos.push({
35442             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35443             y : minY
35444         });
35445
35446         pos.push({
35447             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35448             y : minY + (this.unitWidth + this.gutter) * 2
35449         });
35450         
35451         return pos;
35452         
35453     },
35454     
35455     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35456     {
35457         var pos = [];
35458         
35459         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35460             
35461             pos.push({
35462                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35463                 y : minY
35464             });
35465
35466             pos.push({
35467                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35468                 y : minY + (this.unitWidth + this.gutter) * 1
35469             });
35470             
35471             pos.push({
35472                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35473                 y : minY + (this.unitWidth + this.gutter) * 2
35474             });
35475             
35476             return pos;
35477             
35478         }
35479         
35480         if(box[0].size == 'xs' && box[1].size == 'xs'){
35481             
35482             pos.push({
35483                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35484                 y : minY
35485             });
35486
35487             pos.push({
35488                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35489                 y : minY
35490             });
35491             
35492             pos.push({
35493                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35494                 y : minY + (this.unitWidth + this.gutter) * 1
35495             });
35496             
35497             return pos;
35498             
35499         }
35500         
35501         pos.push({
35502             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35503             y : minY
35504         });
35505
35506         pos.push({
35507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35508             y : minY + (this.unitWidth + this.gutter) * 2
35509         });
35510
35511         pos.push({
35512             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35513             y : minY + (this.unitWidth + this.gutter) * 2
35514         });
35515             
35516         return pos;
35517         
35518     },
35519     
35520     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35521     {
35522         var pos = [];
35523         
35524         if(box[0].size == 'xs'){
35525             
35526             pos.push({
35527                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
35533                 y : minY
35534             });
35535             
35536             pos.push({
35537                 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),
35538                 y : minY
35539             });
35540             
35541             pos.push({
35542                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35543                 y : minY + (this.unitWidth + this.gutter) * 1
35544             });
35545             
35546             return pos;
35547             
35548         }
35549         
35550         pos.push({
35551             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35552             y : minY
35553         });
35554         
35555         pos.push({
35556             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
35562             y : minY + (this.unitWidth + this.gutter) * 2
35563         });
35564         
35565         pos.push({
35566             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),
35567             y : minY + (this.unitWidth + this.gutter) * 2
35568         });
35569
35570         return pos;
35571         
35572     },
35573     
35574     /**
35575     * remove a Masonry Brick
35576     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35577     */
35578     removeBrick : function(brick_id)
35579     {
35580         if (!brick_id) {
35581             return;
35582         }
35583         
35584         for (var i = 0; i<this.bricks.length; i++) {
35585             if (this.bricks[i].id == brick_id) {
35586                 this.bricks.splice(i,1);
35587                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35588                 this.initial();
35589             }
35590         }
35591     },
35592     
35593     /**
35594     * adds a Masonry Brick
35595     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35596     */
35597     addBrick : function(cfg)
35598     {
35599         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35600         //this.register(cn);
35601         cn.parentId = this.id;
35602         cn.render(this.el);
35603         return cn;
35604     },
35605     
35606     /**
35607     * register a Masonry Brick
35608     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35609     */
35610     
35611     register : function(brick)
35612     {
35613         this.bricks.push(brick);
35614         brick.masonryId = this.id;
35615     },
35616     
35617     /**
35618     * clear all the Masonry Brick
35619     */
35620     clearAll : function()
35621     {
35622         this.bricks = [];
35623         //this.getChildContainer().dom.innerHTML = "";
35624         this.el.dom.innerHTML = '';
35625     },
35626     
35627     getSelected : function()
35628     {
35629         if (!this.selectedBrick) {
35630             return false;
35631         }
35632         
35633         return this.selectedBrick;
35634     }
35635 });
35636
35637 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35638     
35639     groups: {},
35640      /**
35641     * register a Masonry Layout
35642     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35643     */
35644     
35645     register : function(layout)
35646     {
35647         this.groups[layout.id] = layout;
35648     },
35649     /**
35650     * fetch a  Masonry Layout based on the masonry layout ID
35651     * @param {string} the masonry layout to add
35652     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35653     */
35654     
35655     get: function(layout_id) {
35656         if (typeof(this.groups[layout_id]) == 'undefined') {
35657             return false;
35658         }
35659         return this.groups[layout_id] ;
35660     }
35661     
35662     
35663     
35664 });
35665
35666  
35667
35668  /**
35669  *
35670  * This is based on 
35671  * http://masonry.desandro.com
35672  *
35673  * The idea is to render all the bricks based on vertical width...
35674  *
35675  * The original code extends 'outlayer' - we might need to use that....
35676  * 
35677  */
35678
35679
35680 /**
35681  * @class Roo.bootstrap.LayoutMasonryAuto
35682  * @extends Roo.bootstrap.Component
35683  * Bootstrap Layout Masonry class
35684  * 
35685  * @constructor
35686  * Create a new Element
35687  * @param {Object} config The config object
35688  */
35689
35690 Roo.bootstrap.LayoutMasonryAuto = function(config){
35691     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35692 };
35693
35694 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35695     
35696       /**
35697      * @cfg {Boolean} isFitWidth  - resize the width..
35698      */   
35699     isFitWidth : false,  // options..
35700     /**
35701      * @cfg {Boolean} isOriginLeft = left align?
35702      */   
35703     isOriginLeft : true,
35704     /**
35705      * @cfg {Boolean} isOriginTop = top align?
35706      */   
35707     isOriginTop : false,
35708     /**
35709      * @cfg {Boolean} isLayoutInstant = no animation?
35710      */   
35711     isLayoutInstant : false, // needed?
35712     /**
35713      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35714      */   
35715     isResizingContainer : true,
35716     /**
35717      * @cfg {Number} columnWidth  width of the columns 
35718      */   
35719     
35720     columnWidth : 0,
35721     
35722     /**
35723      * @cfg {Number} maxCols maximum number of columns
35724      */   
35725     
35726     maxCols: 0,
35727     /**
35728      * @cfg {Number} padHeight padding below box..
35729      */   
35730     
35731     padHeight : 10, 
35732     
35733     /**
35734      * @cfg {Boolean} isAutoInitial defalut true
35735      */   
35736     
35737     isAutoInitial : true, 
35738     
35739     // private?
35740     gutter : 0,
35741     
35742     containerWidth: 0,
35743     initialColumnWidth : 0,
35744     currentSize : null,
35745     
35746     colYs : null, // array.
35747     maxY : 0,
35748     padWidth: 10,
35749     
35750     
35751     tag: 'div',
35752     cls: '',
35753     bricks: null, //CompositeElement
35754     cols : 0, // array?
35755     // element : null, // wrapped now this.el
35756     _isLayoutInited : null, 
35757     
35758     
35759     getAutoCreate : function(){
35760         
35761         var cfg = {
35762             tag: this.tag,
35763             cls: 'blog-masonary-wrapper ' + this.cls,
35764             cn : {
35765                 cls : 'mas-boxes masonary'
35766             }
35767         };
35768         
35769         return cfg;
35770     },
35771     
35772     getChildContainer: function( )
35773     {
35774         if (this.boxesEl) {
35775             return this.boxesEl;
35776         }
35777         
35778         this.boxesEl = this.el.select('.mas-boxes').first();
35779         
35780         return this.boxesEl;
35781     },
35782     
35783     
35784     initEvents : function()
35785     {
35786         var _this = this;
35787         
35788         if(this.isAutoInitial){
35789             Roo.log('hook children rendered');
35790             this.on('childrenrendered', function() {
35791                 Roo.log('children rendered');
35792                 _this.initial();
35793             } ,this);
35794         }
35795         
35796     },
35797     
35798     initial : function()
35799     {
35800         this.reloadItems();
35801
35802         this.currentSize = this.el.getBox(true);
35803
35804         /// was window resize... - let's see if this works..
35805         Roo.EventManager.onWindowResize(this.resize, this); 
35806
35807         if(!this.isAutoInitial){
35808             this.layout();
35809             return;
35810         }
35811         
35812         this.layout.defer(500,this);
35813     },
35814     
35815     reloadItems: function()
35816     {
35817         this.bricks = this.el.select('.masonry-brick', true);
35818         
35819         this.bricks.each(function(b) {
35820             //Roo.log(b.getSize());
35821             if (!b.attr('originalwidth')) {
35822                 b.attr('originalwidth',  b.getSize().width);
35823             }
35824             
35825         });
35826         
35827         Roo.log(this.bricks.elements.length);
35828     },
35829     
35830     resize : function()
35831     {
35832         Roo.log('resize');
35833         var cs = this.el.getBox(true);
35834         
35835         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35836             Roo.log("no change in with or X");
35837             return;
35838         }
35839         this.currentSize = cs;
35840         this.layout();
35841     },
35842     
35843     layout : function()
35844     {
35845          Roo.log('layout');
35846         this._resetLayout();
35847         //this._manageStamps();
35848       
35849         // don't animate first layout
35850         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35851         this.layoutItems( isInstant );
35852       
35853         // flag for initalized
35854         this._isLayoutInited = true;
35855     },
35856     
35857     layoutItems : function( isInstant )
35858     {
35859         //var items = this._getItemsForLayout( this.items );
35860         // original code supports filtering layout items.. we just ignore it..
35861         
35862         this._layoutItems( this.bricks , isInstant );
35863       
35864         this._postLayout();
35865     },
35866     _layoutItems : function ( items , isInstant)
35867     {
35868        //this.fireEvent( 'layout', this, items );
35869     
35870
35871         if ( !items || !items.elements.length ) {
35872           // no items, emit event with empty array
35873             return;
35874         }
35875
35876         var queue = [];
35877         items.each(function(item) {
35878             Roo.log("layout item");
35879             Roo.log(item);
35880             // get x/y object from method
35881             var position = this._getItemLayoutPosition( item );
35882             // enqueue
35883             position.item = item;
35884             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35885             queue.push( position );
35886         }, this);
35887       
35888         this._processLayoutQueue( queue );
35889     },
35890     /** Sets position of item in DOM
35891     * @param {Element} item
35892     * @param {Number} x - horizontal position
35893     * @param {Number} y - vertical position
35894     * @param {Boolean} isInstant - disables transitions
35895     */
35896     _processLayoutQueue : function( queue )
35897     {
35898         for ( var i=0, len = queue.length; i < len; i++ ) {
35899             var obj = queue[i];
35900             obj.item.position('absolute');
35901             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35902         }
35903     },
35904       
35905     
35906     /**
35907     * Any logic you want to do after each layout,
35908     * i.e. size the container
35909     */
35910     _postLayout : function()
35911     {
35912         this.resizeContainer();
35913     },
35914     
35915     resizeContainer : function()
35916     {
35917         if ( !this.isResizingContainer ) {
35918             return;
35919         }
35920         var size = this._getContainerSize();
35921         if ( size ) {
35922             this.el.setSize(size.width,size.height);
35923             this.boxesEl.setSize(size.width,size.height);
35924         }
35925     },
35926     
35927     
35928     
35929     _resetLayout : function()
35930     {
35931         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35932         this.colWidth = this.el.getWidth();
35933         //this.gutter = this.el.getWidth(); 
35934         
35935         this.measureColumns();
35936
35937         // reset column Y
35938         var i = this.cols;
35939         this.colYs = [];
35940         while (i--) {
35941             this.colYs.push( 0 );
35942         }
35943     
35944         this.maxY = 0;
35945     },
35946
35947     measureColumns : function()
35948     {
35949         this.getContainerWidth();
35950       // if columnWidth is 0, default to outerWidth of first item
35951         if ( !this.columnWidth ) {
35952             var firstItem = this.bricks.first();
35953             Roo.log(firstItem);
35954             this.columnWidth  = this.containerWidth;
35955             if (firstItem && firstItem.attr('originalwidth') ) {
35956                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35957             }
35958             // columnWidth fall back to item of first element
35959             Roo.log("set column width?");
35960                         this.initialColumnWidth = this.columnWidth  ;
35961
35962             // if first elem has no width, default to size of container
35963             
35964         }
35965         
35966         
35967         if (this.initialColumnWidth) {
35968             this.columnWidth = this.initialColumnWidth;
35969         }
35970         
35971         
35972             
35973         // column width is fixed at the top - however if container width get's smaller we should
35974         // reduce it...
35975         
35976         // this bit calcs how man columns..
35977             
35978         var columnWidth = this.columnWidth += this.gutter;
35979       
35980         // calculate columns
35981         var containerWidth = this.containerWidth + this.gutter;
35982         
35983         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35984         // fix rounding errors, typically with gutters
35985         var excess = columnWidth - containerWidth % columnWidth;
35986         
35987         
35988         // if overshoot is less than a pixel, round up, otherwise floor it
35989         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35990         cols = Math[ mathMethod ]( cols );
35991         this.cols = Math.max( cols, 1 );
35992         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35993         
35994          // padding positioning..
35995         var totalColWidth = this.cols * this.columnWidth;
35996         var padavail = this.containerWidth - totalColWidth;
35997         // so for 2 columns - we need 3 'pads'
35998         
35999         var padNeeded = (1+this.cols) * this.padWidth;
36000         
36001         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36002         
36003         this.columnWidth += padExtra
36004         //this.padWidth = Math.floor(padavail /  ( this.cols));
36005         
36006         // adjust colum width so that padding is fixed??
36007         
36008         // we have 3 columns ... total = width * 3
36009         // we have X left over... that should be used by 
36010         
36011         //if (this.expandC) {
36012             
36013         //}
36014         
36015         
36016         
36017     },
36018     
36019     getContainerWidth : function()
36020     {
36021        /* // container is parent if fit width
36022         var container = this.isFitWidth ? this.element.parentNode : this.element;
36023         // check that this.size and size are there
36024         // IE8 triggers resize on body size change, so they might not be
36025         
36026         var size = getSize( container );  //FIXME
36027         this.containerWidth = size && size.innerWidth; //FIXME
36028         */
36029          
36030         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36031         
36032     },
36033     
36034     _getItemLayoutPosition : function( item )  // what is item?
36035     {
36036         // we resize the item to our columnWidth..
36037       
36038         item.setWidth(this.columnWidth);
36039         item.autoBoxAdjust  = false;
36040         
36041         var sz = item.getSize();
36042  
36043         // how many columns does this brick span
36044         var remainder = this.containerWidth % this.columnWidth;
36045         
36046         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36047         // round if off by 1 pixel, otherwise use ceil
36048         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36049         colSpan = Math.min( colSpan, this.cols );
36050         
36051         // normally this should be '1' as we dont' currently allow multi width columns..
36052         
36053         var colGroup = this._getColGroup( colSpan );
36054         // get the minimum Y value from the columns
36055         var minimumY = Math.min.apply( Math, colGroup );
36056         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36057         
36058         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36059          
36060         // position the brick
36061         var position = {
36062             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36063             y: this.currentSize.y + minimumY + this.padHeight
36064         };
36065         
36066         Roo.log(position);
36067         // apply setHeight to necessary columns
36068         var setHeight = minimumY + sz.height + this.padHeight;
36069         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36070         
36071         var setSpan = this.cols + 1 - colGroup.length;
36072         for ( var i = 0; i < setSpan; i++ ) {
36073           this.colYs[ shortColIndex + i ] = setHeight ;
36074         }
36075       
36076         return position;
36077     },
36078     
36079     /**
36080      * @param {Number} colSpan - number of columns the element spans
36081      * @returns {Array} colGroup
36082      */
36083     _getColGroup : function( colSpan )
36084     {
36085         if ( colSpan < 2 ) {
36086           // if brick spans only one column, use all the column Ys
36087           return this.colYs;
36088         }
36089       
36090         var colGroup = [];
36091         // how many different places could this brick fit horizontally
36092         var groupCount = this.cols + 1 - colSpan;
36093         // for each group potential horizontal position
36094         for ( var i = 0; i < groupCount; i++ ) {
36095           // make an array of colY values for that one group
36096           var groupColYs = this.colYs.slice( i, i + colSpan );
36097           // and get the max value of the array
36098           colGroup[i] = Math.max.apply( Math, groupColYs );
36099         }
36100         return colGroup;
36101     },
36102     /*
36103     _manageStamp : function( stamp )
36104     {
36105         var stampSize =  stamp.getSize();
36106         var offset = stamp.getBox();
36107         // get the columns that this stamp affects
36108         var firstX = this.isOriginLeft ? offset.x : offset.right;
36109         var lastX = firstX + stampSize.width;
36110         var firstCol = Math.floor( firstX / this.columnWidth );
36111         firstCol = Math.max( 0, firstCol );
36112         
36113         var lastCol = Math.floor( lastX / this.columnWidth );
36114         // lastCol should not go over if multiple of columnWidth #425
36115         lastCol -= lastX % this.columnWidth ? 0 : 1;
36116         lastCol = Math.min( this.cols - 1, lastCol );
36117         
36118         // set colYs to bottom of the stamp
36119         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36120             stampSize.height;
36121             
36122         for ( var i = firstCol; i <= lastCol; i++ ) {
36123           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36124         }
36125     },
36126     */
36127     
36128     _getContainerSize : function()
36129     {
36130         this.maxY = Math.max.apply( Math, this.colYs );
36131         var size = {
36132             height: this.maxY
36133         };
36134       
36135         if ( this.isFitWidth ) {
36136             size.width = this._getContainerFitWidth();
36137         }
36138       
36139         return size;
36140     },
36141     
36142     _getContainerFitWidth : function()
36143     {
36144         var unusedCols = 0;
36145         // count unused columns
36146         var i = this.cols;
36147         while ( --i ) {
36148           if ( this.colYs[i] !== 0 ) {
36149             break;
36150           }
36151           unusedCols++;
36152         }
36153         // fit container to columns that have been used
36154         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36155     },
36156     
36157     needsResizeLayout : function()
36158     {
36159         var previousWidth = this.containerWidth;
36160         this.getContainerWidth();
36161         return previousWidth !== this.containerWidth;
36162     }
36163  
36164 });
36165
36166  
36167
36168  /*
36169  * - LGPL
36170  *
36171  * element
36172  * 
36173  */
36174
36175 /**
36176  * @class Roo.bootstrap.MasonryBrick
36177  * @extends Roo.bootstrap.Component
36178  * Bootstrap MasonryBrick class
36179  * 
36180  * @constructor
36181  * Create a new MasonryBrick
36182  * @param {Object} config The config object
36183  */
36184
36185 Roo.bootstrap.MasonryBrick = function(config){
36186     
36187     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36188     
36189     Roo.bootstrap.MasonryBrick.register(this);
36190     
36191     this.addEvents({
36192         // raw events
36193         /**
36194          * @event click
36195          * When a MasonryBrick is clcik
36196          * @param {Roo.bootstrap.MasonryBrick} this
36197          * @param {Roo.EventObject} e
36198          */
36199         "click" : true
36200     });
36201 };
36202
36203 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36204     
36205     /**
36206      * @cfg {String} title
36207      */   
36208     title : '',
36209     /**
36210      * @cfg {String} html
36211      */   
36212     html : '',
36213     /**
36214      * @cfg {String} bgimage
36215      */   
36216     bgimage : '',
36217     /**
36218      * @cfg {String} videourl
36219      */   
36220     videourl : '',
36221     /**
36222      * @cfg {String} cls
36223      */   
36224     cls : '',
36225     /**
36226      * @cfg {String} href
36227      */   
36228     href : '',
36229     /**
36230      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36231      */   
36232     size : 'xs',
36233     
36234     /**
36235      * @cfg {String} placetitle (center|bottom)
36236      */   
36237     placetitle : '',
36238     
36239     /**
36240      * @cfg {Boolean} isFitContainer defalut true
36241      */   
36242     isFitContainer : true, 
36243     
36244     /**
36245      * @cfg {Boolean} preventDefault defalut false
36246      */   
36247     preventDefault : false, 
36248     
36249     /**
36250      * @cfg {Boolean} inverse defalut false
36251      */   
36252     maskInverse : false, 
36253     
36254     getAutoCreate : function()
36255     {
36256         if(!this.isFitContainer){
36257             return this.getSplitAutoCreate();
36258         }
36259         
36260         var cls = 'masonry-brick masonry-brick-full';
36261         
36262         if(this.href.length){
36263             cls += ' masonry-brick-link';
36264         }
36265         
36266         if(this.bgimage.length){
36267             cls += ' masonry-brick-image';
36268         }
36269         
36270         if(this.maskInverse){
36271             cls += ' mask-inverse';
36272         }
36273         
36274         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36275             cls += ' enable-mask';
36276         }
36277         
36278         if(this.size){
36279             cls += ' masonry-' + this.size + '-brick';
36280         }
36281         
36282         if(this.placetitle.length){
36283             
36284             switch (this.placetitle) {
36285                 case 'center' :
36286                     cls += ' masonry-center-title';
36287                     break;
36288                 case 'bottom' :
36289                     cls += ' masonry-bottom-title';
36290                     break;
36291                 default:
36292                     break;
36293             }
36294             
36295         } else {
36296             if(!this.html.length && !this.bgimage.length){
36297                 cls += ' masonry-center-title';
36298             }
36299
36300             if(!this.html.length && this.bgimage.length){
36301                 cls += ' masonry-bottom-title';
36302             }
36303         }
36304         
36305         if(this.cls){
36306             cls += ' ' + this.cls;
36307         }
36308         
36309         var cfg = {
36310             tag: (this.href.length) ? 'a' : 'div',
36311             cls: cls,
36312             cn: [
36313                 {
36314                     tag: 'div',
36315                     cls: 'masonry-brick-mask'
36316                 },
36317                 {
36318                     tag: 'div',
36319                     cls: 'masonry-brick-paragraph',
36320                     cn: []
36321                 }
36322             ]
36323         };
36324         
36325         if(this.href.length){
36326             cfg.href = this.href;
36327         }
36328         
36329         var cn = cfg.cn[1].cn;
36330         
36331         if(this.title.length){
36332             cn.push({
36333                 tag: 'h4',
36334                 cls: 'masonry-brick-title',
36335                 html: this.title
36336             });
36337         }
36338         
36339         if(this.html.length){
36340             cn.push({
36341                 tag: 'p',
36342                 cls: 'masonry-brick-text',
36343                 html: this.html
36344             });
36345         }
36346         
36347         if (!this.title.length && !this.html.length) {
36348             cfg.cn[1].cls += ' hide';
36349         }
36350         
36351         if(this.bgimage.length){
36352             cfg.cn.push({
36353                 tag: 'img',
36354                 cls: 'masonry-brick-image-view',
36355                 src: this.bgimage
36356             });
36357         }
36358         
36359         if(this.videourl.length){
36360             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36361             // youtube support only?
36362             cfg.cn.push({
36363                 tag: 'iframe',
36364                 cls: 'masonry-brick-image-view',
36365                 src: vurl,
36366                 frameborder : 0,
36367                 allowfullscreen : true
36368             });
36369         }
36370         
36371         return cfg;
36372         
36373     },
36374     
36375     getSplitAutoCreate : function()
36376     {
36377         var cls = 'masonry-brick masonry-brick-split';
36378         
36379         if(this.href.length){
36380             cls += ' masonry-brick-link';
36381         }
36382         
36383         if(this.bgimage.length){
36384             cls += ' masonry-brick-image';
36385         }
36386         
36387         if(this.size){
36388             cls += ' masonry-' + this.size + '-brick';
36389         }
36390         
36391         switch (this.placetitle) {
36392             case 'center' :
36393                 cls += ' masonry-center-title';
36394                 break;
36395             case 'bottom' :
36396                 cls += ' masonry-bottom-title';
36397                 break;
36398             default:
36399                 if(!this.bgimage.length){
36400                     cls += ' masonry-center-title';
36401                 }
36402
36403                 if(this.bgimage.length){
36404                     cls += ' masonry-bottom-title';
36405                 }
36406                 break;
36407         }
36408         
36409         if(this.cls){
36410             cls += ' ' + this.cls;
36411         }
36412         
36413         var cfg = {
36414             tag: (this.href.length) ? 'a' : 'div',
36415             cls: cls,
36416             cn: [
36417                 {
36418                     tag: 'div',
36419                     cls: 'masonry-brick-split-head',
36420                     cn: [
36421                         {
36422                             tag: 'div',
36423                             cls: 'masonry-brick-paragraph',
36424                             cn: []
36425                         }
36426                     ]
36427                 },
36428                 {
36429                     tag: 'div',
36430                     cls: 'masonry-brick-split-body',
36431                     cn: []
36432                 }
36433             ]
36434         };
36435         
36436         if(this.href.length){
36437             cfg.href = this.href;
36438         }
36439         
36440         if(this.title.length){
36441             cfg.cn[0].cn[0].cn.push({
36442                 tag: 'h4',
36443                 cls: 'masonry-brick-title',
36444                 html: this.title
36445             });
36446         }
36447         
36448         if(this.html.length){
36449             cfg.cn[1].cn.push({
36450                 tag: 'p',
36451                 cls: 'masonry-brick-text',
36452                 html: this.html
36453             });
36454         }
36455
36456         if(this.bgimage.length){
36457             cfg.cn[0].cn.push({
36458                 tag: 'img',
36459                 cls: 'masonry-brick-image-view',
36460                 src: this.bgimage
36461             });
36462         }
36463         
36464         if(this.videourl.length){
36465             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36466             // youtube support only?
36467             cfg.cn[0].cn.cn.push({
36468                 tag: 'iframe',
36469                 cls: 'masonry-brick-image-view',
36470                 src: vurl,
36471                 frameborder : 0,
36472                 allowfullscreen : true
36473             });
36474         }
36475         
36476         return cfg;
36477     },
36478     
36479     initEvents: function() 
36480     {
36481         switch (this.size) {
36482             case 'xs' :
36483                 this.x = 1;
36484                 this.y = 1;
36485                 break;
36486             case 'sm' :
36487                 this.x = 2;
36488                 this.y = 2;
36489                 break;
36490             case 'md' :
36491             case 'md-left' :
36492             case 'md-right' :
36493                 this.x = 3;
36494                 this.y = 3;
36495                 break;
36496             case 'tall' :
36497                 this.x = 2;
36498                 this.y = 3;
36499                 break;
36500             case 'wide' :
36501                 this.x = 3;
36502                 this.y = 2;
36503                 break;
36504             case 'wide-thin' :
36505                 this.x = 3;
36506                 this.y = 1;
36507                 break;
36508                         
36509             default :
36510                 break;
36511         }
36512         
36513         if(Roo.isTouch){
36514             this.el.on('touchstart', this.onTouchStart, this);
36515             this.el.on('touchmove', this.onTouchMove, this);
36516             this.el.on('touchend', this.onTouchEnd, this);
36517             this.el.on('contextmenu', this.onContextMenu, this);
36518         } else {
36519             this.el.on('mouseenter'  ,this.enter, this);
36520             this.el.on('mouseleave', this.leave, this);
36521             this.el.on('click', this.onClick, this);
36522         }
36523         
36524         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36525             this.parent().bricks.push(this);   
36526         }
36527         
36528     },
36529     
36530     onClick: function(e, el)
36531     {
36532         var time = this.endTimer - this.startTimer;
36533         // Roo.log(e.preventDefault());
36534         if(Roo.isTouch){
36535             if(time > 1000){
36536                 e.preventDefault();
36537                 return;
36538             }
36539         }
36540         
36541         if(!this.preventDefault){
36542             return;
36543         }
36544         
36545         e.preventDefault();
36546         
36547         if (this.activeClass != '') {
36548             this.selectBrick();
36549         }
36550         
36551         this.fireEvent('click', this, e);
36552     },
36553     
36554     enter: function(e, el)
36555     {
36556         e.preventDefault();
36557         
36558         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36559             return;
36560         }
36561         
36562         if(this.bgimage.length && this.html.length){
36563             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36564         }
36565     },
36566     
36567     leave: function(e, el)
36568     {
36569         e.preventDefault();
36570         
36571         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36572             return;
36573         }
36574         
36575         if(this.bgimage.length && this.html.length){
36576             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36577         }
36578     },
36579     
36580     onTouchStart: function(e, el)
36581     {
36582 //        e.preventDefault();
36583         
36584         this.touchmoved = false;
36585         
36586         if(!this.isFitContainer){
36587             return;
36588         }
36589         
36590         if(!this.bgimage.length || !this.html.length){
36591             return;
36592         }
36593         
36594         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36595         
36596         this.timer = new Date().getTime();
36597         
36598     },
36599     
36600     onTouchMove: function(e, el)
36601     {
36602         this.touchmoved = true;
36603     },
36604     
36605     onContextMenu : function(e,el)
36606     {
36607         e.preventDefault();
36608         e.stopPropagation();
36609         return false;
36610     },
36611     
36612     onTouchEnd: function(e, el)
36613     {
36614 //        e.preventDefault();
36615         
36616         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36617         
36618             this.leave(e,el);
36619             
36620             return;
36621         }
36622         
36623         if(!this.bgimage.length || !this.html.length){
36624             
36625             if(this.href.length){
36626                 window.location.href = this.href;
36627             }
36628             
36629             return;
36630         }
36631         
36632         if(!this.isFitContainer){
36633             return;
36634         }
36635         
36636         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36637         
36638         window.location.href = this.href;
36639     },
36640     
36641     //selection on single brick only
36642     selectBrick : function() {
36643         
36644         if (!this.parentId) {
36645             return;
36646         }
36647         
36648         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36649         var index = m.selectedBrick.indexOf(this.id);
36650         
36651         if ( index > -1) {
36652             m.selectedBrick.splice(index,1);
36653             this.el.removeClass(this.activeClass);
36654             return;
36655         }
36656         
36657         for(var i = 0; i < m.selectedBrick.length; i++) {
36658             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36659             b.el.removeClass(b.activeClass);
36660         }
36661         
36662         m.selectedBrick = [];
36663         
36664         m.selectedBrick.push(this.id);
36665         this.el.addClass(this.activeClass);
36666         return;
36667     },
36668     
36669     isSelected : function(){
36670         return this.el.hasClass(this.activeClass);
36671         
36672     }
36673 });
36674
36675 Roo.apply(Roo.bootstrap.MasonryBrick, {
36676     
36677     //groups: {},
36678     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36679      /**
36680     * register a Masonry Brick
36681     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36682     */
36683     
36684     register : function(brick)
36685     {
36686         //this.groups[brick.id] = brick;
36687         this.groups.add(brick.id, brick);
36688     },
36689     /**
36690     * fetch a  masonry brick based on the masonry brick ID
36691     * @param {string} the masonry brick to add
36692     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36693     */
36694     
36695     get: function(brick_id) 
36696     {
36697         // if (typeof(this.groups[brick_id]) == 'undefined') {
36698         //     return false;
36699         // }
36700         // return this.groups[brick_id] ;
36701         
36702         if(this.groups.key(brick_id)) {
36703             return this.groups.key(brick_id);
36704         }
36705         
36706         return false;
36707     }
36708     
36709     
36710     
36711 });
36712
36713  /*
36714  * - LGPL
36715  *
36716  * element
36717  * 
36718  */
36719
36720 /**
36721  * @class Roo.bootstrap.Brick
36722  * @extends Roo.bootstrap.Component
36723  * Bootstrap Brick class
36724  * 
36725  * @constructor
36726  * Create a new Brick
36727  * @param {Object} config The config object
36728  */
36729
36730 Roo.bootstrap.Brick = function(config){
36731     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36732     
36733     this.addEvents({
36734         // raw events
36735         /**
36736          * @event click
36737          * When a Brick is click
36738          * @param {Roo.bootstrap.Brick} this
36739          * @param {Roo.EventObject} e
36740          */
36741         "click" : true
36742     });
36743 };
36744
36745 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36746     
36747     /**
36748      * @cfg {String} title
36749      */   
36750     title : '',
36751     /**
36752      * @cfg {String} html
36753      */   
36754     html : '',
36755     /**
36756      * @cfg {String} bgimage
36757      */   
36758     bgimage : '',
36759     /**
36760      * @cfg {String} cls
36761      */   
36762     cls : '',
36763     /**
36764      * @cfg {String} href
36765      */   
36766     href : '',
36767     /**
36768      * @cfg {String} video
36769      */   
36770     video : '',
36771     /**
36772      * @cfg {Boolean} square
36773      */   
36774     square : true,
36775     
36776     getAutoCreate : function()
36777     {
36778         var cls = 'roo-brick';
36779         
36780         if(this.href.length){
36781             cls += ' roo-brick-link';
36782         }
36783         
36784         if(this.bgimage.length){
36785             cls += ' roo-brick-image';
36786         }
36787         
36788         if(!this.html.length && !this.bgimage.length){
36789             cls += ' roo-brick-center-title';
36790         }
36791         
36792         if(!this.html.length && this.bgimage.length){
36793             cls += ' roo-brick-bottom-title';
36794         }
36795         
36796         if(this.cls){
36797             cls += ' ' + this.cls;
36798         }
36799         
36800         var cfg = {
36801             tag: (this.href.length) ? 'a' : 'div',
36802             cls: cls,
36803             cn: [
36804                 {
36805                     tag: 'div',
36806                     cls: 'roo-brick-paragraph',
36807                     cn: []
36808                 }
36809             ]
36810         };
36811         
36812         if(this.href.length){
36813             cfg.href = this.href;
36814         }
36815         
36816         var cn = cfg.cn[0].cn;
36817         
36818         if(this.title.length){
36819             cn.push({
36820                 tag: 'h4',
36821                 cls: 'roo-brick-title',
36822                 html: this.title
36823             });
36824         }
36825         
36826         if(this.html.length){
36827             cn.push({
36828                 tag: 'p',
36829                 cls: 'roo-brick-text',
36830                 html: this.html
36831             });
36832         } else {
36833             cn.cls += ' hide';
36834         }
36835         
36836         if(this.bgimage.length){
36837             cfg.cn.push({
36838                 tag: 'img',
36839                 cls: 'roo-brick-image-view',
36840                 src: this.bgimage
36841             });
36842         }
36843         
36844         return cfg;
36845     },
36846     
36847     initEvents: function() 
36848     {
36849         if(this.title.length || this.html.length){
36850             this.el.on('mouseenter'  ,this.enter, this);
36851             this.el.on('mouseleave', this.leave, this);
36852         }
36853         
36854         Roo.EventManager.onWindowResize(this.resize, this); 
36855         
36856         if(this.bgimage.length){
36857             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36858             this.imageEl.on('load', this.onImageLoad, this);
36859             return;
36860         }
36861         
36862         this.resize();
36863     },
36864     
36865     onImageLoad : function()
36866     {
36867         this.resize();
36868     },
36869     
36870     resize : function()
36871     {
36872         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36873         
36874         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36875         
36876         if(this.bgimage.length){
36877             var image = this.el.select('.roo-brick-image-view', true).first();
36878             
36879             image.setWidth(paragraph.getWidth());
36880             
36881             if(this.square){
36882                 image.setHeight(paragraph.getWidth());
36883             }
36884             
36885             this.el.setHeight(image.getHeight());
36886             paragraph.setHeight(image.getHeight());
36887             
36888         }
36889         
36890     },
36891     
36892     enter: function(e, el)
36893     {
36894         e.preventDefault();
36895         
36896         if(this.bgimage.length){
36897             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36898             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36899         }
36900     },
36901     
36902     leave: function(e, el)
36903     {
36904         e.preventDefault();
36905         
36906         if(this.bgimage.length){
36907             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36908             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36909         }
36910     }
36911     
36912 });
36913
36914  
36915
36916  /*
36917  * - LGPL
36918  *
36919  * Number field 
36920  */
36921
36922 /**
36923  * @class Roo.bootstrap.NumberField
36924  * @extends Roo.bootstrap.Input
36925  * Bootstrap NumberField class
36926  * 
36927  * 
36928  * 
36929  * 
36930  * @constructor
36931  * Create a new NumberField
36932  * @param {Object} config The config object
36933  */
36934
36935 Roo.bootstrap.NumberField = function(config){
36936     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36937 };
36938
36939 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36940     
36941     /**
36942      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36943      */
36944     allowDecimals : true,
36945     /**
36946      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36947      */
36948     decimalSeparator : ".",
36949     /**
36950      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36951      */
36952     decimalPrecision : 2,
36953     /**
36954      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36955      */
36956     allowNegative : true,
36957     
36958     /**
36959      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36960      */
36961     allowZero: true,
36962     /**
36963      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36964      */
36965     minValue : Number.NEGATIVE_INFINITY,
36966     /**
36967      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36968      */
36969     maxValue : Number.MAX_VALUE,
36970     /**
36971      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36972      */
36973     minText : "The minimum value for this field is {0}",
36974     /**
36975      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36976      */
36977     maxText : "The maximum value for this field is {0}",
36978     /**
36979      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36980      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36981      */
36982     nanText : "{0} is not a valid number",
36983     /**
36984      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36985      */
36986     thousandsDelimiter : false,
36987     /**
36988      * @cfg {String} valueAlign alignment of value
36989      */
36990     valueAlign : "left",
36991
36992     getAutoCreate : function()
36993     {
36994         var hiddenInput = {
36995             tag: 'input',
36996             type: 'hidden',
36997             id: Roo.id(),
36998             cls: 'hidden-number-input'
36999         };
37000         
37001         if (this.name) {
37002             hiddenInput.name = this.name;
37003         }
37004         
37005         this.name = '';
37006         
37007         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37008         
37009         this.name = hiddenInput.name;
37010         
37011         if(cfg.cn.length > 0) {
37012             cfg.cn.push(hiddenInput);
37013         }
37014         
37015         return cfg;
37016     },
37017
37018     // private
37019     initEvents : function()
37020     {   
37021         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37022         
37023         var allowed = "0123456789";
37024         
37025         if(this.allowDecimals){
37026             allowed += this.decimalSeparator;
37027         }
37028         
37029         if(this.allowNegative){
37030             allowed += "-";
37031         }
37032         
37033         if(this.thousandsDelimiter) {
37034             allowed += ",";
37035         }
37036         
37037         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37038         
37039         var keyPress = function(e){
37040             
37041             var k = e.getKey();
37042             
37043             var c = e.getCharCode();
37044             
37045             if(
37046                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37047                     allowed.indexOf(String.fromCharCode(c)) === -1
37048             ){
37049                 e.stopEvent();
37050                 return;
37051             }
37052             
37053             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37054                 return;
37055             }
37056             
37057             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37058                 e.stopEvent();
37059             }
37060         };
37061         
37062         this.el.on("keypress", keyPress, this);
37063     },
37064     
37065     validateValue : function(value)
37066     {
37067         
37068         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37069             return false;
37070         }
37071         
37072         var num = this.parseValue(value);
37073         
37074         if(isNaN(num)){
37075             this.markInvalid(String.format(this.nanText, value));
37076             return false;
37077         }
37078         
37079         if(num < this.minValue){
37080             this.markInvalid(String.format(this.minText, this.minValue));
37081             return false;
37082         }
37083         
37084         if(num > this.maxValue){
37085             this.markInvalid(String.format(this.maxText, this.maxValue));
37086             return false;
37087         }
37088         
37089         return true;
37090     },
37091
37092     getValue : function()
37093     {
37094         var v = this.hiddenEl().getValue();
37095         
37096         return this.fixPrecision(this.parseValue(v));
37097     },
37098
37099     parseValue : function(value)
37100     {
37101         if(this.thousandsDelimiter) {
37102             value += "";
37103             r = new RegExp(",", "g");
37104             value = value.replace(r, "");
37105         }
37106         
37107         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37108         return isNaN(value) ? '' : value;
37109     },
37110
37111     fixPrecision : function(value)
37112     {
37113         if(this.thousandsDelimiter) {
37114             value += "";
37115             r = new RegExp(",", "g");
37116             value = value.replace(r, "");
37117         }
37118         
37119         var nan = isNaN(value);
37120         
37121         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37122             return nan ? '' : value;
37123         }
37124         return parseFloat(value).toFixed(this.decimalPrecision);
37125     },
37126
37127     setValue : function(v)
37128     {
37129         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37130         
37131         this.value = v;
37132         
37133         if(this.rendered){
37134             
37135             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37136             
37137             this.inputEl().dom.value = (v == '') ? '' :
37138                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37139             
37140             if(!this.allowZero && v === '0') {
37141                 this.hiddenEl().dom.value = '';
37142                 this.inputEl().dom.value = '';
37143             }
37144             
37145             this.validate();
37146         }
37147     },
37148
37149     decimalPrecisionFcn : function(v)
37150     {
37151         return Math.floor(v);
37152     },
37153
37154     beforeBlur : function()
37155     {
37156         var v = this.parseValue(this.getRawValue());
37157         
37158         if(v || v === 0 || v === ''){
37159             this.setValue(v);
37160         }
37161     },
37162     
37163     hiddenEl : function()
37164     {
37165         return this.el.select('input.hidden-number-input',true).first();
37166     }
37167     
37168 });
37169
37170  
37171
37172 /*
37173 * Licence: LGPL
37174 */
37175
37176 /**
37177  * @class Roo.bootstrap.DocumentSlider
37178  * @extends Roo.bootstrap.Component
37179  * Bootstrap DocumentSlider class
37180  * 
37181  * @constructor
37182  * Create a new DocumentViewer
37183  * @param {Object} config The config object
37184  */
37185
37186 Roo.bootstrap.DocumentSlider = function(config){
37187     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37188     
37189     this.files = [];
37190     
37191     this.addEvents({
37192         /**
37193          * @event initial
37194          * Fire after initEvent
37195          * @param {Roo.bootstrap.DocumentSlider} this
37196          */
37197         "initial" : true,
37198         /**
37199          * @event update
37200          * Fire after update
37201          * @param {Roo.bootstrap.DocumentSlider} this
37202          */
37203         "update" : true,
37204         /**
37205          * @event click
37206          * Fire after click
37207          * @param {Roo.bootstrap.DocumentSlider} this
37208          */
37209         "click" : true
37210     });
37211 };
37212
37213 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37214     
37215     files : false,
37216     
37217     indicator : 0,
37218     
37219     getAutoCreate : function()
37220     {
37221         var cfg = {
37222             tag : 'div',
37223             cls : 'roo-document-slider',
37224             cn : [
37225                 {
37226                     tag : 'div',
37227                     cls : 'roo-document-slider-header',
37228                     cn : [
37229                         {
37230                             tag : 'div',
37231                             cls : 'roo-document-slider-header-title'
37232                         }
37233                     ]
37234                 },
37235                 {
37236                     tag : 'div',
37237                     cls : 'roo-document-slider-body',
37238                     cn : [
37239                         {
37240                             tag : 'div',
37241                             cls : 'roo-document-slider-prev',
37242                             cn : [
37243                                 {
37244                                     tag : 'i',
37245                                     cls : 'fa fa-chevron-left'
37246                                 }
37247                             ]
37248                         },
37249                         {
37250                             tag : 'div',
37251                             cls : 'roo-document-slider-thumb',
37252                             cn : [
37253                                 {
37254                                     tag : 'img',
37255                                     cls : 'roo-document-slider-image'
37256                                 }
37257                             ]
37258                         },
37259                         {
37260                             tag : 'div',
37261                             cls : 'roo-document-slider-next',
37262                             cn : [
37263                                 {
37264                                     tag : 'i',
37265                                     cls : 'fa fa-chevron-right'
37266                                 }
37267                             ]
37268                         }
37269                     ]
37270                 }
37271             ]
37272         };
37273         
37274         return cfg;
37275     },
37276     
37277     initEvents : function()
37278     {
37279         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37280         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37281         
37282         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37283         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37284         
37285         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37286         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37287         
37288         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37289         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37290         
37291         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37292         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37293         
37294         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37295         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37296         
37297         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37298         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37299         
37300         this.thumbEl.on('click', this.onClick, this);
37301         
37302         this.prevIndicator.on('click', this.prev, this);
37303         
37304         this.nextIndicator.on('click', this.next, this);
37305         
37306     },
37307     
37308     initial : function()
37309     {
37310         if(this.files.length){
37311             this.indicator = 1;
37312             this.update()
37313         }
37314         
37315         this.fireEvent('initial', this);
37316     },
37317     
37318     update : function()
37319     {
37320         this.imageEl.attr('src', this.files[this.indicator - 1]);
37321         
37322         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37323         
37324         this.prevIndicator.show();
37325         
37326         if(this.indicator == 1){
37327             this.prevIndicator.hide();
37328         }
37329         
37330         this.nextIndicator.show();
37331         
37332         if(this.indicator == this.files.length){
37333             this.nextIndicator.hide();
37334         }
37335         
37336         this.thumbEl.scrollTo('top');
37337         
37338         this.fireEvent('update', this);
37339     },
37340     
37341     onClick : function(e)
37342     {
37343         e.preventDefault();
37344         
37345         this.fireEvent('click', this);
37346     },
37347     
37348     prev : function(e)
37349     {
37350         e.preventDefault();
37351         
37352         this.indicator = Math.max(1, this.indicator - 1);
37353         
37354         this.update();
37355     },
37356     
37357     next : function(e)
37358     {
37359         e.preventDefault();
37360         
37361         this.indicator = Math.min(this.files.length, this.indicator + 1);
37362         
37363         this.update();
37364     }
37365 });
37366 /*
37367  * - LGPL
37368  *
37369  * RadioSet
37370  *
37371  *
37372  */
37373
37374 /**
37375  * @class Roo.bootstrap.RadioSet
37376  * @extends Roo.bootstrap.Input
37377  * Bootstrap RadioSet class
37378  * @cfg {String} indicatorpos (left|right) default left
37379  * @cfg {Boolean} inline (true|false) inline the element (default true)
37380  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37381  * @constructor
37382  * Create a new RadioSet
37383  * @param {Object} config The config object
37384  */
37385
37386 Roo.bootstrap.RadioSet = function(config){
37387     
37388     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37389     
37390     this.radioes = [];
37391     
37392     Roo.bootstrap.RadioSet.register(this);
37393     
37394     this.addEvents({
37395         /**
37396         * @event check
37397         * Fires when the element is checked or unchecked.
37398         * @param {Roo.bootstrap.RadioSet} this This radio
37399         * @param {Roo.bootstrap.Radio} item The checked item
37400         */
37401        check : true,
37402        /**
37403         * @event click
37404         * Fires when the element is click.
37405         * @param {Roo.bootstrap.RadioSet} this This radio set
37406         * @param {Roo.bootstrap.Radio} item The checked item
37407         * @param {Roo.EventObject} e The event object
37408         */
37409        click : true
37410     });
37411     
37412 };
37413
37414 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37415
37416     radioes : false,
37417     
37418     inline : true,
37419     
37420     weight : '',
37421     
37422     indicatorpos : 'left',
37423     
37424     getAutoCreate : function()
37425     {
37426         var label = {
37427             tag : 'label',
37428             cls : 'roo-radio-set-label',
37429             cn : [
37430                 {
37431                     tag : 'span',
37432                     html : this.fieldLabel
37433                 }
37434             ]
37435         };
37436         if (Roo.bootstrap.version == 3) {
37437             
37438             
37439             if(this.indicatorpos == 'left'){
37440                 label.cn.unshift({
37441                     tag : 'i',
37442                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37443                     tooltip : 'This field is required'
37444                 });
37445             } else {
37446                 label.cn.push({
37447                     tag : 'i',
37448                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37449                     tooltip : 'This field is required'
37450                 });
37451             }
37452         }
37453         var items = {
37454             tag : 'div',
37455             cls : 'roo-radio-set-items'
37456         };
37457         
37458         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37459         
37460         if (align === 'left' && this.fieldLabel.length) {
37461             
37462             items = {
37463                 cls : "roo-radio-set-right", 
37464                 cn: [
37465                     items
37466                 ]
37467             };
37468             
37469             if(this.labelWidth > 12){
37470                 label.style = "width: " + this.labelWidth + 'px';
37471             }
37472             
37473             if(this.labelWidth < 13 && this.labelmd == 0){
37474                 this.labelmd = this.labelWidth;
37475             }
37476             
37477             if(this.labellg > 0){
37478                 label.cls += ' col-lg-' + this.labellg;
37479                 items.cls += ' col-lg-' + (12 - this.labellg);
37480             }
37481             
37482             if(this.labelmd > 0){
37483                 label.cls += ' col-md-' + this.labelmd;
37484                 items.cls += ' col-md-' + (12 - this.labelmd);
37485             }
37486             
37487             if(this.labelsm > 0){
37488                 label.cls += ' col-sm-' + this.labelsm;
37489                 items.cls += ' col-sm-' + (12 - this.labelsm);
37490             }
37491             
37492             if(this.labelxs > 0){
37493                 label.cls += ' col-xs-' + this.labelxs;
37494                 items.cls += ' col-xs-' + (12 - this.labelxs);
37495             }
37496         }
37497         
37498         var cfg = {
37499             tag : 'div',
37500             cls : 'roo-radio-set',
37501             cn : [
37502                 {
37503                     tag : 'input',
37504                     cls : 'roo-radio-set-input',
37505                     type : 'hidden',
37506                     name : this.name,
37507                     value : this.value ? this.value :  ''
37508                 },
37509                 label,
37510                 items
37511             ]
37512         };
37513         
37514         if(this.weight.length){
37515             cfg.cls += ' roo-radio-' + this.weight;
37516         }
37517         
37518         if(this.inline) {
37519             cfg.cls += ' roo-radio-set-inline';
37520         }
37521         
37522         var settings=this;
37523         ['xs','sm','md','lg'].map(function(size){
37524             if (settings[size]) {
37525                 cfg.cls += ' col-' + size + '-' + settings[size];
37526             }
37527         });
37528         
37529         return cfg;
37530         
37531     },
37532
37533     initEvents : function()
37534     {
37535         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37536         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37537         
37538         if(!this.fieldLabel.length){
37539             this.labelEl.hide();
37540         }
37541         
37542         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37543         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37544         
37545         this.indicator = this.indicatorEl();
37546         
37547         if(this.indicator){
37548             this.indicator.addClass('invisible');
37549         }
37550         
37551         this.originalValue = this.getValue();
37552         
37553     },
37554     
37555     inputEl: function ()
37556     {
37557         return this.el.select('.roo-radio-set-input', true).first();
37558     },
37559     
37560     getChildContainer : function()
37561     {
37562         return this.itemsEl;
37563     },
37564     
37565     register : function(item)
37566     {
37567         this.radioes.push(item);
37568         
37569     },
37570     
37571     validate : function()
37572     {   
37573         if(this.getVisibilityEl().hasClass('hidden')){
37574             return true;
37575         }
37576         
37577         var valid = false;
37578         
37579         Roo.each(this.radioes, function(i){
37580             if(!i.checked){
37581                 return;
37582             }
37583             
37584             valid = true;
37585             return false;
37586         });
37587         
37588         if(this.allowBlank) {
37589             return true;
37590         }
37591         
37592         if(this.disabled || valid){
37593             this.markValid();
37594             return true;
37595         }
37596         
37597         this.markInvalid();
37598         return false;
37599         
37600     },
37601     
37602     markValid : function()
37603     {
37604         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37605             this.indicatorEl().removeClass('visible');
37606             this.indicatorEl().addClass('invisible');
37607         }
37608         
37609         
37610         if (Roo.bootstrap.version == 3) {
37611             this.el.removeClass([this.invalidClass, this.validClass]);
37612             this.el.addClass(this.validClass);
37613         } else {
37614             this.el.removeClass(['is-invalid','is-valid']);
37615             this.el.addClass(['is-valid']);
37616         }
37617         this.fireEvent('valid', this);
37618     },
37619     
37620     markInvalid : function(msg)
37621     {
37622         if(this.allowBlank || this.disabled){
37623             return;
37624         }
37625         
37626         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37627             this.indicatorEl().removeClass('invisible');
37628             this.indicatorEl().addClass('visible');
37629         }
37630         if (Roo.bootstrap.version == 3) {
37631             this.el.removeClass([this.invalidClass, this.validClass]);
37632             this.el.addClass(this.invalidClass);
37633         } else {
37634             this.el.removeClass(['is-invalid','is-valid']);
37635             this.el.addClass(['is-invalid']);
37636         }
37637         
37638         this.fireEvent('invalid', this, msg);
37639         
37640     },
37641     
37642     setValue : function(v, suppressEvent)
37643     {   
37644         if(this.value === v){
37645             return;
37646         }
37647         
37648         this.value = v;
37649         
37650         if(this.rendered){
37651             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37652         }
37653         
37654         Roo.each(this.radioes, function(i){
37655             i.checked = false;
37656             i.el.removeClass('checked');
37657         });
37658         
37659         Roo.each(this.radioes, function(i){
37660             
37661             if(i.value === v || i.value.toString() === v.toString()){
37662                 i.checked = true;
37663                 i.el.addClass('checked');
37664                 
37665                 if(suppressEvent !== true){
37666                     this.fireEvent('check', this, i);
37667                 }
37668                 
37669                 return false;
37670             }
37671             
37672         }, this);
37673         
37674         this.validate();
37675     },
37676     
37677     clearInvalid : function(){
37678         
37679         if(!this.el || this.preventMark){
37680             return;
37681         }
37682         
37683         this.el.removeClass([this.invalidClass]);
37684         
37685         this.fireEvent('valid', this);
37686     }
37687     
37688 });
37689
37690 Roo.apply(Roo.bootstrap.RadioSet, {
37691     
37692     groups: {},
37693     
37694     register : function(set)
37695     {
37696         this.groups[set.name] = set;
37697     },
37698     
37699     get: function(name) 
37700     {
37701         if (typeof(this.groups[name]) == 'undefined') {
37702             return false;
37703         }
37704         
37705         return this.groups[name] ;
37706     }
37707     
37708 });
37709 /*
37710  * Based on:
37711  * Ext JS Library 1.1.1
37712  * Copyright(c) 2006-2007, Ext JS, LLC.
37713  *
37714  * Originally Released Under LGPL - original licence link has changed is not relivant.
37715  *
37716  * Fork - LGPL
37717  * <script type="text/javascript">
37718  */
37719
37720
37721 /**
37722  * @class Roo.bootstrap.SplitBar
37723  * @extends Roo.util.Observable
37724  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37725  * <br><br>
37726  * Usage:
37727  * <pre><code>
37728 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37729                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37730 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37731 split.minSize = 100;
37732 split.maxSize = 600;
37733 split.animate = true;
37734 split.on('moved', splitterMoved);
37735 </code></pre>
37736  * @constructor
37737  * Create a new SplitBar
37738  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37739  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37740  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37741  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37742                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37743                         position of the SplitBar).
37744  */
37745 Roo.bootstrap.SplitBar = function(cfg){
37746     
37747     /** @private */
37748     
37749     //{
37750     //  dragElement : elm
37751     //  resizingElement: el,
37752         // optional..
37753     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37754     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37755         // existingProxy ???
37756     //}
37757     
37758     this.el = Roo.get(cfg.dragElement, true);
37759     this.el.dom.unselectable = "on";
37760     /** @private */
37761     this.resizingEl = Roo.get(cfg.resizingElement, true);
37762
37763     /**
37764      * @private
37765      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37766      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37767      * @type Number
37768      */
37769     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37770     
37771     /**
37772      * The minimum size of the resizing element. (Defaults to 0)
37773      * @type Number
37774      */
37775     this.minSize = 0;
37776     
37777     /**
37778      * The maximum size of the resizing element. (Defaults to 2000)
37779      * @type Number
37780      */
37781     this.maxSize = 2000;
37782     
37783     /**
37784      * Whether to animate the transition to the new size
37785      * @type Boolean
37786      */
37787     this.animate = false;
37788     
37789     /**
37790      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37791      * @type Boolean
37792      */
37793     this.useShim = false;
37794     
37795     /** @private */
37796     this.shim = null;
37797     
37798     if(!cfg.existingProxy){
37799         /** @private */
37800         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37801     }else{
37802         this.proxy = Roo.get(cfg.existingProxy).dom;
37803     }
37804     /** @private */
37805     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37806     
37807     /** @private */
37808     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37809     
37810     /** @private */
37811     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37812     
37813     /** @private */
37814     this.dragSpecs = {};
37815     
37816     /**
37817      * @private The adapter to use to positon and resize elements
37818      */
37819     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37820     this.adapter.init(this);
37821     
37822     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37823         /** @private */
37824         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37825         this.el.addClass("roo-splitbar-h");
37826     }else{
37827         /** @private */
37828         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37829         this.el.addClass("roo-splitbar-v");
37830     }
37831     
37832     this.addEvents({
37833         /**
37834          * @event resize
37835          * Fires when the splitter is moved (alias for {@link #event-moved})
37836          * @param {Roo.bootstrap.SplitBar} this
37837          * @param {Number} newSize the new width or height
37838          */
37839         "resize" : true,
37840         /**
37841          * @event moved
37842          * Fires when the splitter is moved
37843          * @param {Roo.bootstrap.SplitBar} this
37844          * @param {Number} newSize the new width or height
37845          */
37846         "moved" : true,
37847         /**
37848          * @event beforeresize
37849          * Fires before the splitter is dragged
37850          * @param {Roo.bootstrap.SplitBar} this
37851          */
37852         "beforeresize" : true,
37853
37854         "beforeapply" : true
37855     });
37856
37857     Roo.util.Observable.call(this);
37858 };
37859
37860 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37861     onStartProxyDrag : function(x, y){
37862         this.fireEvent("beforeresize", this);
37863         if(!this.overlay){
37864             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37865             o.unselectable();
37866             o.enableDisplayMode("block");
37867             // all splitbars share the same overlay
37868             Roo.bootstrap.SplitBar.prototype.overlay = o;
37869         }
37870         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37871         this.overlay.show();
37872         Roo.get(this.proxy).setDisplayed("block");
37873         var size = this.adapter.getElementSize(this);
37874         this.activeMinSize = this.getMinimumSize();;
37875         this.activeMaxSize = this.getMaximumSize();;
37876         var c1 = size - this.activeMinSize;
37877         var c2 = Math.max(this.activeMaxSize - size, 0);
37878         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37879             this.dd.resetConstraints();
37880             this.dd.setXConstraint(
37881                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37882                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37883             );
37884             this.dd.setYConstraint(0, 0);
37885         }else{
37886             this.dd.resetConstraints();
37887             this.dd.setXConstraint(0, 0);
37888             this.dd.setYConstraint(
37889                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37890                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37891             );
37892          }
37893         this.dragSpecs.startSize = size;
37894         this.dragSpecs.startPoint = [x, y];
37895         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37896     },
37897     
37898     /** 
37899      * @private Called after the drag operation by the DDProxy
37900      */
37901     onEndProxyDrag : function(e){
37902         Roo.get(this.proxy).setDisplayed(false);
37903         var endPoint = Roo.lib.Event.getXY(e);
37904         if(this.overlay){
37905             this.overlay.hide();
37906         }
37907         var newSize;
37908         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37909             newSize = this.dragSpecs.startSize + 
37910                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37911                     endPoint[0] - this.dragSpecs.startPoint[0] :
37912                     this.dragSpecs.startPoint[0] - endPoint[0]
37913                 );
37914         }else{
37915             newSize = this.dragSpecs.startSize + 
37916                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37917                     endPoint[1] - this.dragSpecs.startPoint[1] :
37918                     this.dragSpecs.startPoint[1] - endPoint[1]
37919                 );
37920         }
37921         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37922         if(newSize != this.dragSpecs.startSize){
37923             if(this.fireEvent('beforeapply', this, newSize) !== false){
37924                 this.adapter.setElementSize(this, newSize);
37925                 this.fireEvent("moved", this, newSize);
37926                 this.fireEvent("resize", this, newSize);
37927             }
37928         }
37929     },
37930     
37931     /**
37932      * Get the adapter this SplitBar uses
37933      * @return The adapter object
37934      */
37935     getAdapter : function(){
37936         return this.adapter;
37937     },
37938     
37939     /**
37940      * Set the adapter this SplitBar uses
37941      * @param {Object} adapter A SplitBar adapter object
37942      */
37943     setAdapter : function(adapter){
37944         this.adapter = adapter;
37945         this.adapter.init(this);
37946     },
37947     
37948     /**
37949      * Gets the minimum size for the resizing element
37950      * @return {Number} The minimum size
37951      */
37952     getMinimumSize : function(){
37953         return this.minSize;
37954     },
37955     
37956     /**
37957      * Sets the minimum size for the resizing element
37958      * @param {Number} minSize The minimum size
37959      */
37960     setMinimumSize : function(minSize){
37961         this.minSize = minSize;
37962     },
37963     
37964     /**
37965      * Gets the maximum size for the resizing element
37966      * @return {Number} The maximum size
37967      */
37968     getMaximumSize : function(){
37969         return this.maxSize;
37970     },
37971     
37972     /**
37973      * Sets the maximum size for the resizing element
37974      * @param {Number} maxSize The maximum size
37975      */
37976     setMaximumSize : function(maxSize){
37977         this.maxSize = maxSize;
37978     },
37979     
37980     /**
37981      * Sets the initialize size for the resizing element
37982      * @param {Number} size The initial size
37983      */
37984     setCurrentSize : function(size){
37985         var oldAnimate = this.animate;
37986         this.animate = false;
37987         this.adapter.setElementSize(this, size);
37988         this.animate = oldAnimate;
37989     },
37990     
37991     /**
37992      * Destroy this splitbar. 
37993      * @param {Boolean} removeEl True to remove the element
37994      */
37995     destroy : function(removeEl){
37996         if(this.shim){
37997             this.shim.remove();
37998         }
37999         this.dd.unreg();
38000         this.proxy.parentNode.removeChild(this.proxy);
38001         if(removeEl){
38002             this.el.remove();
38003         }
38004     }
38005 });
38006
38007 /**
38008  * @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.
38009  */
38010 Roo.bootstrap.SplitBar.createProxy = function(dir){
38011     var proxy = new Roo.Element(document.createElement("div"));
38012     proxy.unselectable();
38013     var cls = 'roo-splitbar-proxy';
38014     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38015     document.body.appendChild(proxy.dom);
38016     return proxy.dom;
38017 };
38018
38019 /** 
38020  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38021  * Default Adapter. It assumes the splitter and resizing element are not positioned
38022  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38023  */
38024 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38025 };
38026
38027 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38028     // do nothing for now
38029     init : function(s){
38030     
38031     },
38032     /**
38033      * Called before drag operations to get the current size of the resizing element. 
38034      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38035      */
38036      getElementSize : function(s){
38037         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38038             return s.resizingEl.getWidth();
38039         }else{
38040             return s.resizingEl.getHeight();
38041         }
38042     },
38043     
38044     /**
38045      * Called after drag operations to set the size of the resizing element.
38046      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38047      * @param {Number} newSize The new size to set
38048      * @param {Function} onComplete A function to be invoked when resizing is complete
38049      */
38050     setElementSize : function(s, newSize, onComplete){
38051         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38052             if(!s.animate){
38053                 s.resizingEl.setWidth(newSize);
38054                 if(onComplete){
38055                     onComplete(s, newSize);
38056                 }
38057             }else{
38058                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38059             }
38060         }else{
38061             
38062             if(!s.animate){
38063                 s.resizingEl.setHeight(newSize);
38064                 if(onComplete){
38065                     onComplete(s, newSize);
38066                 }
38067             }else{
38068                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38069             }
38070         }
38071     }
38072 };
38073
38074 /** 
38075  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38076  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38077  * Adapter that  moves the splitter element to align with the resized sizing element. 
38078  * Used with an absolute positioned SplitBar.
38079  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38080  * document.body, make sure you assign an id to the body element.
38081  */
38082 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38083     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38084     this.container = Roo.get(container);
38085 };
38086
38087 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38088     init : function(s){
38089         this.basic.init(s);
38090     },
38091     
38092     getElementSize : function(s){
38093         return this.basic.getElementSize(s);
38094     },
38095     
38096     setElementSize : function(s, newSize, onComplete){
38097         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38098     },
38099     
38100     moveSplitter : function(s){
38101         var yes = Roo.bootstrap.SplitBar;
38102         switch(s.placement){
38103             case yes.LEFT:
38104                 s.el.setX(s.resizingEl.getRight());
38105                 break;
38106             case yes.RIGHT:
38107                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38108                 break;
38109             case yes.TOP:
38110                 s.el.setY(s.resizingEl.getBottom());
38111                 break;
38112             case yes.BOTTOM:
38113                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38114                 break;
38115         }
38116     }
38117 };
38118
38119 /**
38120  * Orientation constant - Create a vertical SplitBar
38121  * @static
38122  * @type Number
38123  */
38124 Roo.bootstrap.SplitBar.VERTICAL = 1;
38125
38126 /**
38127  * Orientation constant - Create a horizontal SplitBar
38128  * @static
38129  * @type Number
38130  */
38131 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38132
38133 /**
38134  * Placement constant - The resizing element is to the left of the splitter element
38135  * @static
38136  * @type Number
38137  */
38138 Roo.bootstrap.SplitBar.LEFT = 1;
38139
38140 /**
38141  * Placement constant - The resizing element is to the right of the splitter element
38142  * @static
38143  * @type Number
38144  */
38145 Roo.bootstrap.SplitBar.RIGHT = 2;
38146
38147 /**
38148  * Placement constant - The resizing element is positioned above the splitter element
38149  * @static
38150  * @type Number
38151  */
38152 Roo.bootstrap.SplitBar.TOP = 3;
38153
38154 /**
38155  * Placement constant - The resizing element is positioned under splitter element
38156  * @static
38157  * @type Number
38158  */
38159 Roo.bootstrap.SplitBar.BOTTOM = 4;
38160 Roo.namespace("Roo.bootstrap.layout");/*
38161  * Based on:
38162  * Ext JS Library 1.1.1
38163  * Copyright(c) 2006-2007, Ext JS, LLC.
38164  *
38165  * Originally Released Under LGPL - original licence link has changed is not relivant.
38166  *
38167  * Fork - LGPL
38168  * <script type="text/javascript">
38169  */
38170
38171 /**
38172  * @class Roo.bootstrap.layout.Manager
38173  * @extends Roo.bootstrap.Component
38174  * Base class for layout managers.
38175  */
38176 Roo.bootstrap.layout.Manager = function(config)
38177 {
38178     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38179
38180
38181
38182
38183
38184     /** false to disable window resize monitoring @type Boolean */
38185     this.monitorWindowResize = true;
38186     this.regions = {};
38187     this.addEvents({
38188         /**
38189          * @event layout
38190          * Fires when a layout is performed.
38191          * @param {Roo.LayoutManager} this
38192          */
38193         "layout" : true,
38194         /**
38195          * @event regionresized
38196          * Fires when the user resizes a region.
38197          * @param {Roo.LayoutRegion} region The resized region
38198          * @param {Number} newSize The new size (width for east/west, height for north/south)
38199          */
38200         "regionresized" : true,
38201         /**
38202          * @event regioncollapsed
38203          * Fires when a region is collapsed.
38204          * @param {Roo.LayoutRegion} region The collapsed region
38205          */
38206         "regioncollapsed" : true,
38207         /**
38208          * @event regionexpanded
38209          * Fires when a region is expanded.
38210          * @param {Roo.LayoutRegion} region The expanded region
38211          */
38212         "regionexpanded" : true
38213     });
38214     this.updating = false;
38215
38216     if (config.el) {
38217         this.el = Roo.get(config.el);
38218         this.initEvents();
38219     }
38220
38221 };
38222
38223 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38224
38225
38226     regions : null,
38227
38228     monitorWindowResize : true,
38229
38230
38231     updating : false,
38232
38233
38234     onRender : function(ct, position)
38235     {
38236         if(!this.el){
38237             this.el = Roo.get(ct);
38238             this.initEvents();
38239         }
38240         //this.fireEvent('render',this);
38241     },
38242
38243
38244     initEvents: function()
38245     {
38246
38247
38248         // ie scrollbar fix
38249         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38250             document.body.scroll = "no";
38251         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38252             this.el.position('relative');
38253         }
38254         this.id = this.el.id;
38255         this.el.addClass("roo-layout-container");
38256         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38257         if(this.el.dom != document.body ) {
38258             this.el.on('resize', this.layout,this);
38259             this.el.on('show', this.layout,this);
38260         }
38261
38262     },
38263
38264     /**
38265      * Returns true if this layout is currently being updated
38266      * @return {Boolean}
38267      */
38268     isUpdating : function(){
38269         return this.updating;
38270     },
38271
38272     /**
38273      * Suspend the LayoutManager from doing auto-layouts while
38274      * making multiple add or remove calls
38275      */
38276     beginUpdate : function(){
38277         this.updating = true;
38278     },
38279
38280     /**
38281      * Restore auto-layouts and optionally disable the manager from performing a layout
38282      * @param {Boolean} noLayout true to disable a layout update
38283      */
38284     endUpdate : function(noLayout){
38285         this.updating = false;
38286         if(!noLayout){
38287             this.layout();
38288         }
38289     },
38290
38291     layout: function(){
38292         // abstract...
38293     },
38294
38295     onRegionResized : function(region, newSize){
38296         this.fireEvent("regionresized", region, newSize);
38297         this.layout();
38298     },
38299
38300     onRegionCollapsed : function(region){
38301         this.fireEvent("regioncollapsed", region);
38302     },
38303
38304     onRegionExpanded : function(region){
38305         this.fireEvent("regionexpanded", region);
38306     },
38307
38308     /**
38309      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38310      * performs box-model adjustments.
38311      * @return {Object} The size as an object {width: (the width), height: (the height)}
38312      */
38313     getViewSize : function()
38314     {
38315         var size;
38316         if(this.el.dom != document.body){
38317             size = this.el.getSize();
38318         }else{
38319             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38320         }
38321         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38322         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38323         return size;
38324     },
38325
38326     /**
38327      * Returns the Element this layout is bound to.
38328      * @return {Roo.Element}
38329      */
38330     getEl : function(){
38331         return this.el;
38332     },
38333
38334     /**
38335      * Returns the specified region.
38336      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38337      * @return {Roo.LayoutRegion}
38338      */
38339     getRegion : function(target){
38340         return this.regions[target.toLowerCase()];
38341     },
38342
38343     onWindowResize : function(){
38344         if(this.monitorWindowResize){
38345             this.layout();
38346         }
38347     }
38348 });
38349 /*
38350  * Based on:
38351  * Ext JS Library 1.1.1
38352  * Copyright(c) 2006-2007, Ext JS, LLC.
38353  *
38354  * Originally Released Under LGPL - original licence link has changed is not relivant.
38355  *
38356  * Fork - LGPL
38357  * <script type="text/javascript">
38358  */
38359 /**
38360  * @class Roo.bootstrap.layout.Border
38361  * @extends Roo.bootstrap.layout.Manager
38362  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38363  * please see: examples/bootstrap/nested.html<br><br>
38364  
38365 <b>The container the layout is rendered into can be either the body element or any other element.
38366 If it is not the body element, the container needs to either be an absolute positioned element,
38367 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38368 the container size if it is not the body element.</b>
38369
38370 * @constructor
38371 * Create a new Border
38372 * @param {Object} config Configuration options
38373  */
38374 Roo.bootstrap.layout.Border = function(config){
38375     config = config || {};
38376     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38377     
38378     
38379     
38380     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38381         if(config[region]){
38382             config[region].region = region;
38383             this.addRegion(config[region]);
38384         }
38385     },this);
38386     
38387 };
38388
38389 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38390
38391 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38392     
38393     parent : false, // this might point to a 'nest' or a ???
38394     
38395     /**
38396      * Creates and adds a new region if it doesn't already exist.
38397      * @param {String} target The target region key (north, south, east, west or center).
38398      * @param {Object} config The regions config object
38399      * @return {BorderLayoutRegion} The new region
38400      */
38401     addRegion : function(config)
38402     {
38403         if(!this.regions[config.region]){
38404             var r = this.factory(config);
38405             this.bindRegion(r);
38406         }
38407         return this.regions[config.region];
38408     },
38409
38410     // private (kinda)
38411     bindRegion : function(r){
38412         this.regions[r.config.region] = r;
38413         
38414         r.on("visibilitychange",    this.layout, this);
38415         r.on("paneladded",          this.layout, this);
38416         r.on("panelremoved",        this.layout, this);
38417         r.on("invalidated",         this.layout, this);
38418         r.on("resized",             this.onRegionResized, this);
38419         r.on("collapsed",           this.onRegionCollapsed, this);
38420         r.on("expanded",            this.onRegionExpanded, this);
38421     },
38422
38423     /**
38424      * Performs a layout update.
38425      */
38426     layout : function()
38427     {
38428         if(this.updating) {
38429             return;
38430         }
38431         
38432         // render all the rebions if they have not been done alreayd?
38433         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38434             if(this.regions[region] && !this.regions[region].bodyEl){
38435                 this.regions[region].onRender(this.el)
38436             }
38437         },this);
38438         
38439         var size = this.getViewSize();
38440         var w = size.width;
38441         var h = size.height;
38442         var centerW = w;
38443         var centerH = h;
38444         var centerY = 0;
38445         var centerX = 0;
38446         //var x = 0, y = 0;
38447
38448         var rs = this.regions;
38449         var north = rs["north"];
38450         var south = rs["south"]; 
38451         var west = rs["west"];
38452         var east = rs["east"];
38453         var center = rs["center"];
38454         //if(this.hideOnLayout){ // not supported anymore
38455             //c.el.setStyle("display", "none");
38456         //}
38457         if(north && north.isVisible()){
38458             var b = north.getBox();
38459             var m = north.getMargins();
38460             b.width = w - (m.left+m.right);
38461             b.x = m.left;
38462             b.y = m.top;
38463             centerY = b.height + b.y + m.bottom;
38464             centerH -= centerY;
38465             north.updateBox(this.safeBox(b));
38466         }
38467         if(south && south.isVisible()){
38468             var b = south.getBox();
38469             var m = south.getMargins();
38470             b.width = w - (m.left+m.right);
38471             b.x = m.left;
38472             var totalHeight = (b.height + m.top + m.bottom);
38473             b.y = h - totalHeight + m.top;
38474             centerH -= totalHeight;
38475             south.updateBox(this.safeBox(b));
38476         }
38477         if(west && west.isVisible()){
38478             var b = west.getBox();
38479             var m = west.getMargins();
38480             b.height = centerH - (m.top+m.bottom);
38481             b.x = m.left;
38482             b.y = centerY + m.top;
38483             var totalWidth = (b.width + m.left + m.right);
38484             centerX += totalWidth;
38485             centerW -= totalWidth;
38486             west.updateBox(this.safeBox(b));
38487         }
38488         if(east && east.isVisible()){
38489             var b = east.getBox();
38490             var m = east.getMargins();
38491             b.height = centerH - (m.top+m.bottom);
38492             var totalWidth = (b.width + m.left + m.right);
38493             b.x = w - totalWidth + m.left;
38494             b.y = centerY + m.top;
38495             centerW -= totalWidth;
38496             east.updateBox(this.safeBox(b));
38497         }
38498         if(center){
38499             var m = center.getMargins();
38500             var centerBox = {
38501                 x: centerX + m.left,
38502                 y: centerY + m.top,
38503                 width: centerW - (m.left+m.right),
38504                 height: centerH - (m.top+m.bottom)
38505             };
38506             //if(this.hideOnLayout){
38507                 //center.el.setStyle("display", "block");
38508             //}
38509             center.updateBox(this.safeBox(centerBox));
38510         }
38511         this.el.repaint();
38512         this.fireEvent("layout", this);
38513     },
38514
38515     // private
38516     safeBox : function(box){
38517         box.width = Math.max(0, box.width);
38518         box.height = Math.max(0, box.height);
38519         return box;
38520     },
38521
38522     /**
38523      * Adds a ContentPanel (or subclass) to this layout.
38524      * @param {String} target The target region key (north, south, east, west or center).
38525      * @param {Roo.ContentPanel} panel The panel to add
38526      * @return {Roo.ContentPanel} The added panel
38527      */
38528     add : function(target, panel){
38529          
38530         target = target.toLowerCase();
38531         return this.regions[target].add(panel);
38532     },
38533
38534     /**
38535      * Remove a ContentPanel (or subclass) to this layout.
38536      * @param {String} target The target region key (north, south, east, west or center).
38537      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38538      * @return {Roo.ContentPanel} The removed panel
38539      */
38540     remove : function(target, panel){
38541         target = target.toLowerCase();
38542         return this.regions[target].remove(panel);
38543     },
38544
38545     /**
38546      * Searches all regions for a panel with the specified id
38547      * @param {String} panelId
38548      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38549      */
38550     findPanel : function(panelId){
38551         var rs = this.regions;
38552         for(var target in rs){
38553             if(typeof rs[target] != "function"){
38554                 var p = rs[target].getPanel(panelId);
38555                 if(p){
38556                     return p;
38557                 }
38558             }
38559         }
38560         return null;
38561     },
38562
38563     /**
38564      * Searches all regions for a panel with the specified id and activates (shows) it.
38565      * @param {String/ContentPanel} panelId The panels id or the panel itself
38566      * @return {Roo.ContentPanel} The shown panel or null
38567      */
38568     showPanel : function(panelId) {
38569       var rs = this.regions;
38570       for(var target in rs){
38571          var r = rs[target];
38572          if(typeof r != "function"){
38573             if(r.hasPanel(panelId)){
38574                return r.showPanel(panelId);
38575             }
38576          }
38577       }
38578       return null;
38579    },
38580
38581    /**
38582      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38583      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38584      */
38585    /*
38586     restoreState : function(provider){
38587         if(!provider){
38588             provider = Roo.state.Manager;
38589         }
38590         var sm = new Roo.LayoutStateManager();
38591         sm.init(this, provider);
38592     },
38593 */
38594  
38595  
38596     /**
38597      * Adds a xtype elements to the layout.
38598      * <pre><code>
38599
38600 layout.addxtype({
38601        xtype : 'ContentPanel',
38602        region: 'west',
38603        items: [ .... ]
38604    }
38605 );
38606
38607 layout.addxtype({
38608         xtype : 'NestedLayoutPanel',
38609         region: 'west',
38610         layout: {
38611            center: { },
38612            west: { }   
38613         },
38614         items : [ ... list of content panels or nested layout panels.. ]
38615    }
38616 );
38617 </code></pre>
38618      * @param {Object} cfg Xtype definition of item to add.
38619      */
38620     addxtype : function(cfg)
38621     {
38622         // basically accepts a pannel...
38623         // can accept a layout region..!?!?
38624         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38625         
38626         
38627         // theory?  children can only be panels??
38628         
38629         //if (!cfg.xtype.match(/Panel$/)) {
38630         //    return false;
38631         //}
38632         var ret = false;
38633         
38634         if (typeof(cfg.region) == 'undefined') {
38635             Roo.log("Failed to add Panel, region was not set");
38636             Roo.log(cfg);
38637             return false;
38638         }
38639         var region = cfg.region;
38640         delete cfg.region;
38641         
38642           
38643         var xitems = [];
38644         if (cfg.items) {
38645             xitems = cfg.items;
38646             delete cfg.items;
38647         }
38648         var nb = false;
38649         
38650         if ( region == 'center') {
38651             Roo.log("Center: " + cfg.title);
38652         }
38653         
38654         
38655         switch(cfg.xtype) 
38656         {
38657             case 'Content':  // ContentPanel (el, cfg)
38658             case 'Scroll':  // ContentPanel (el, cfg)
38659             case 'View': 
38660                 cfg.autoCreate = cfg.autoCreate || true;
38661                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38662                 //} else {
38663                 //    var el = this.el.createChild();
38664                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38665                 //}
38666                 
38667                 this.add(region, ret);
38668                 break;
38669             
38670             /*
38671             case 'TreePanel': // our new panel!
38672                 cfg.el = this.el.createChild();
38673                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38674                 this.add(region, ret);
38675                 break;
38676             */
38677             
38678             case 'Nest': 
38679                 // create a new Layout (which is  a Border Layout...
38680                 
38681                 var clayout = cfg.layout;
38682                 clayout.el  = this.el.createChild();
38683                 clayout.items   = clayout.items  || [];
38684                 
38685                 delete cfg.layout;
38686                 
38687                 // replace this exitems with the clayout ones..
38688                 xitems = clayout.items;
38689                  
38690                 // force background off if it's in center...
38691                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38692                     cfg.background = false;
38693                 }
38694                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38695                 
38696                 
38697                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38698                 //console.log('adding nested layout panel '  + cfg.toSource());
38699                 this.add(region, ret);
38700                 nb = {}; /// find first...
38701                 break;
38702             
38703             case 'Grid':
38704                 
38705                 // needs grid and region
38706                 
38707                 //var el = this.getRegion(region).el.createChild();
38708                 /*
38709                  *var el = this.el.createChild();
38710                 // create the grid first...
38711                 cfg.grid.container = el;
38712                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38713                 */
38714                 
38715                 if (region == 'center' && this.active ) {
38716                     cfg.background = false;
38717                 }
38718                 
38719                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38720                 
38721                 this.add(region, ret);
38722                 /*
38723                 if (cfg.background) {
38724                     // render grid on panel activation (if panel background)
38725                     ret.on('activate', function(gp) {
38726                         if (!gp.grid.rendered) {
38727                     //        gp.grid.render(el);
38728                         }
38729                     });
38730                 } else {
38731                   //  cfg.grid.render(el);
38732                 }
38733                 */
38734                 break;
38735            
38736            
38737             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38738                 // it was the old xcomponent building that caused this before.
38739                 // espeically if border is the top element in the tree.
38740                 ret = this;
38741                 break; 
38742                 
38743                     
38744                 
38745                 
38746                 
38747             default:
38748                 /*
38749                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38750                     
38751                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38752                     this.add(region, ret);
38753                 } else {
38754                 */
38755                     Roo.log(cfg);
38756                     throw "Can not add '" + cfg.xtype + "' to Border";
38757                     return null;
38758              
38759                                 
38760              
38761         }
38762         this.beginUpdate();
38763         // add children..
38764         var region = '';
38765         var abn = {};
38766         Roo.each(xitems, function(i)  {
38767             region = nb && i.region ? i.region : false;
38768             
38769             var add = ret.addxtype(i);
38770            
38771             if (region) {
38772                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38773                 if (!i.background) {
38774                     abn[region] = nb[region] ;
38775                 }
38776             }
38777             
38778         });
38779         this.endUpdate();
38780
38781         // make the last non-background panel active..
38782         //if (nb) { Roo.log(abn); }
38783         if (nb) {
38784             
38785             for(var r in abn) {
38786                 region = this.getRegion(r);
38787                 if (region) {
38788                     // tried using nb[r], but it does not work..
38789                      
38790                     region.showPanel(abn[r]);
38791                    
38792                 }
38793             }
38794         }
38795         return ret;
38796         
38797     },
38798     
38799     
38800 // private
38801     factory : function(cfg)
38802     {
38803         
38804         var validRegions = Roo.bootstrap.layout.Border.regions;
38805
38806         var target = cfg.region;
38807         cfg.mgr = this;
38808         
38809         var r = Roo.bootstrap.layout;
38810         Roo.log(target);
38811         switch(target){
38812             case "north":
38813                 return new r.North(cfg);
38814             case "south":
38815                 return new r.South(cfg);
38816             case "east":
38817                 return new r.East(cfg);
38818             case "west":
38819                 return new r.West(cfg);
38820             case "center":
38821                 return new r.Center(cfg);
38822         }
38823         throw 'Layout region "'+target+'" not supported.';
38824     }
38825     
38826     
38827 });
38828  /*
38829  * Based on:
38830  * Ext JS Library 1.1.1
38831  * Copyright(c) 2006-2007, Ext JS, LLC.
38832  *
38833  * Originally Released Under LGPL - original licence link has changed is not relivant.
38834  *
38835  * Fork - LGPL
38836  * <script type="text/javascript">
38837  */
38838  
38839 /**
38840  * @class Roo.bootstrap.layout.Basic
38841  * @extends Roo.util.Observable
38842  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38843  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38844  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38845  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38846  * @cfg {string}   region  the region that it inhabits..
38847  * @cfg {bool}   skipConfig skip config?
38848  * 
38849
38850  */
38851 Roo.bootstrap.layout.Basic = function(config){
38852     
38853     this.mgr = config.mgr;
38854     
38855     this.position = config.region;
38856     
38857     var skipConfig = config.skipConfig;
38858     
38859     this.events = {
38860         /**
38861          * @scope Roo.BasicLayoutRegion
38862          */
38863         
38864         /**
38865          * @event beforeremove
38866          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38867          * @param {Roo.LayoutRegion} this
38868          * @param {Roo.ContentPanel} panel The panel
38869          * @param {Object} e The cancel event object
38870          */
38871         "beforeremove" : true,
38872         /**
38873          * @event invalidated
38874          * Fires when the layout for this region is changed.
38875          * @param {Roo.LayoutRegion} this
38876          */
38877         "invalidated" : true,
38878         /**
38879          * @event visibilitychange
38880          * Fires when this region is shown or hidden 
38881          * @param {Roo.LayoutRegion} this
38882          * @param {Boolean} visibility true or false
38883          */
38884         "visibilitychange" : true,
38885         /**
38886          * @event paneladded
38887          * Fires when a panel is added. 
38888          * @param {Roo.LayoutRegion} this
38889          * @param {Roo.ContentPanel} panel The panel
38890          */
38891         "paneladded" : true,
38892         /**
38893          * @event panelremoved
38894          * Fires when a panel is removed. 
38895          * @param {Roo.LayoutRegion} this
38896          * @param {Roo.ContentPanel} panel The panel
38897          */
38898         "panelremoved" : true,
38899         /**
38900          * @event beforecollapse
38901          * Fires when this region before collapse.
38902          * @param {Roo.LayoutRegion} this
38903          */
38904         "beforecollapse" : true,
38905         /**
38906          * @event collapsed
38907          * Fires when this region is collapsed.
38908          * @param {Roo.LayoutRegion} this
38909          */
38910         "collapsed" : true,
38911         /**
38912          * @event expanded
38913          * Fires when this region is expanded.
38914          * @param {Roo.LayoutRegion} this
38915          */
38916         "expanded" : true,
38917         /**
38918          * @event slideshow
38919          * Fires when this region is slid into view.
38920          * @param {Roo.LayoutRegion} this
38921          */
38922         "slideshow" : true,
38923         /**
38924          * @event slidehide
38925          * Fires when this region slides out of view. 
38926          * @param {Roo.LayoutRegion} this
38927          */
38928         "slidehide" : true,
38929         /**
38930          * @event panelactivated
38931          * Fires when a panel is activated. 
38932          * @param {Roo.LayoutRegion} this
38933          * @param {Roo.ContentPanel} panel The activated panel
38934          */
38935         "panelactivated" : true,
38936         /**
38937          * @event resized
38938          * Fires when the user resizes this region. 
38939          * @param {Roo.LayoutRegion} this
38940          * @param {Number} newSize The new size (width for east/west, height for north/south)
38941          */
38942         "resized" : true
38943     };
38944     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38945     this.panels = new Roo.util.MixedCollection();
38946     this.panels.getKey = this.getPanelId.createDelegate(this);
38947     this.box = null;
38948     this.activePanel = null;
38949     // ensure listeners are added...
38950     
38951     if (config.listeners || config.events) {
38952         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38953             listeners : config.listeners || {},
38954             events : config.events || {}
38955         });
38956     }
38957     
38958     if(skipConfig !== true){
38959         this.applyConfig(config);
38960     }
38961 };
38962
38963 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38964 {
38965     getPanelId : function(p){
38966         return p.getId();
38967     },
38968     
38969     applyConfig : function(config){
38970         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38971         this.config = config;
38972         
38973     },
38974     
38975     /**
38976      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38977      * the width, for horizontal (north, south) the height.
38978      * @param {Number} newSize The new width or height
38979      */
38980     resizeTo : function(newSize){
38981         var el = this.el ? this.el :
38982                  (this.activePanel ? this.activePanel.getEl() : null);
38983         if(el){
38984             switch(this.position){
38985                 case "east":
38986                 case "west":
38987                     el.setWidth(newSize);
38988                     this.fireEvent("resized", this, newSize);
38989                 break;
38990                 case "north":
38991                 case "south":
38992                     el.setHeight(newSize);
38993                     this.fireEvent("resized", this, newSize);
38994                 break;                
38995             }
38996         }
38997     },
38998     
38999     getBox : function(){
39000         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39001     },
39002     
39003     getMargins : function(){
39004         return this.margins;
39005     },
39006     
39007     updateBox : function(box){
39008         this.box = box;
39009         var el = this.activePanel.getEl();
39010         el.dom.style.left = box.x + "px";
39011         el.dom.style.top = box.y + "px";
39012         this.activePanel.setSize(box.width, box.height);
39013     },
39014     
39015     /**
39016      * Returns the container element for this region.
39017      * @return {Roo.Element}
39018      */
39019     getEl : function(){
39020         return this.activePanel;
39021     },
39022     
39023     /**
39024      * Returns true if this region is currently visible.
39025      * @return {Boolean}
39026      */
39027     isVisible : function(){
39028         return this.activePanel ? true : false;
39029     },
39030     
39031     setActivePanel : function(panel){
39032         panel = this.getPanel(panel);
39033         if(this.activePanel && this.activePanel != panel){
39034             this.activePanel.setActiveState(false);
39035             this.activePanel.getEl().setLeftTop(-10000,-10000);
39036         }
39037         this.activePanel = panel;
39038         panel.setActiveState(true);
39039         if(this.box){
39040             panel.setSize(this.box.width, this.box.height);
39041         }
39042         this.fireEvent("panelactivated", this, panel);
39043         this.fireEvent("invalidated");
39044     },
39045     
39046     /**
39047      * Show the specified panel.
39048      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39049      * @return {Roo.ContentPanel} The shown panel or null
39050      */
39051     showPanel : function(panel){
39052         panel = this.getPanel(panel);
39053         if(panel){
39054             this.setActivePanel(panel);
39055         }
39056         return panel;
39057     },
39058     
39059     /**
39060      * Get the active panel for this region.
39061      * @return {Roo.ContentPanel} The active panel or null
39062      */
39063     getActivePanel : function(){
39064         return this.activePanel;
39065     },
39066     
39067     /**
39068      * Add the passed ContentPanel(s)
39069      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39070      * @return {Roo.ContentPanel} The panel added (if only one was added)
39071      */
39072     add : function(panel){
39073         if(arguments.length > 1){
39074             for(var i = 0, len = arguments.length; i < len; i++) {
39075                 this.add(arguments[i]);
39076             }
39077             return null;
39078         }
39079         if(this.hasPanel(panel)){
39080             this.showPanel(panel);
39081             return panel;
39082         }
39083         var el = panel.getEl();
39084         if(el.dom.parentNode != this.mgr.el.dom){
39085             this.mgr.el.dom.appendChild(el.dom);
39086         }
39087         if(panel.setRegion){
39088             panel.setRegion(this);
39089         }
39090         this.panels.add(panel);
39091         el.setStyle("position", "absolute");
39092         if(!panel.background){
39093             this.setActivePanel(panel);
39094             if(this.config.initialSize && this.panels.getCount()==1){
39095                 this.resizeTo(this.config.initialSize);
39096             }
39097         }
39098         this.fireEvent("paneladded", this, panel);
39099         return panel;
39100     },
39101     
39102     /**
39103      * Returns true if the panel is in this region.
39104      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39105      * @return {Boolean}
39106      */
39107     hasPanel : function(panel){
39108         if(typeof panel == "object"){ // must be panel obj
39109             panel = panel.getId();
39110         }
39111         return this.getPanel(panel) ? true : false;
39112     },
39113     
39114     /**
39115      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39116      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39117      * @param {Boolean} preservePanel Overrides the config preservePanel option
39118      * @return {Roo.ContentPanel} The panel that was removed
39119      */
39120     remove : function(panel, preservePanel){
39121         panel = this.getPanel(panel);
39122         if(!panel){
39123             return null;
39124         }
39125         var e = {};
39126         this.fireEvent("beforeremove", this, panel, e);
39127         if(e.cancel === true){
39128             return null;
39129         }
39130         var panelId = panel.getId();
39131         this.panels.removeKey(panelId);
39132         return panel;
39133     },
39134     
39135     /**
39136      * Returns the panel specified or null if it's not in this region.
39137      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39138      * @return {Roo.ContentPanel}
39139      */
39140     getPanel : function(id){
39141         if(typeof id == "object"){ // must be panel obj
39142             return id;
39143         }
39144         return this.panels.get(id);
39145     },
39146     
39147     /**
39148      * Returns this regions position (north/south/east/west/center).
39149      * @return {String} 
39150      */
39151     getPosition: function(){
39152         return this.position;    
39153     }
39154 });/*
39155  * Based on:
39156  * Ext JS Library 1.1.1
39157  * Copyright(c) 2006-2007, Ext JS, LLC.
39158  *
39159  * Originally Released Under LGPL - original licence link has changed is not relivant.
39160  *
39161  * Fork - LGPL
39162  * <script type="text/javascript">
39163  */
39164  
39165 /**
39166  * @class Roo.bootstrap.layout.Region
39167  * @extends Roo.bootstrap.layout.Basic
39168  * This class represents a region in a layout manager.
39169  
39170  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39171  * @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})
39172  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39173  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39174  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39175  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39176  * @cfg {String}    title           The title for the region (overrides panel titles)
39177  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39178  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39179  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39180  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39181  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39182  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39183  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39184  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39185  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39186  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39187
39188  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39189  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39190  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39191  * @cfg {Number}    width           For East/West panels
39192  * @cfg {Number}    height          For North/South panels
39193  * @cfg {Boolean}   split           To show the splitter
39194  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39195  * 
39196  * @cfg {string}   cls             Extra CSS classes to add to region
39197  * 
39198  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39199  * @cfg {string}   region  the region that it inhabits..
39200  *
39201
39202  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39203  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39204
39205  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39206  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39207  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39208  */
39209 Roo.bootstrap.layout.Region = function(config)
39210 {
39211     this.applyConfig(config);
39212
39213     var mgr = config.mgr;
39214     var pos = config.region;
39215     config.skipConfig = true;
39216     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39217     
39218     if (mgr.el) {
39219         this.onRender(mgr.el);   
39220     }
39221      
39222     this.visible = true;
39223     this.collapsed = false;
39224     this.unrendered_panels = [];
39225 };
39226
39227 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39228
39229     position: '', // set by wrapper (eg. north/south etc..)
39230     unrendered_panels : null,  // unrendered panels.
39231     
39232     tabPosition : false,
39233     
39234     mgr: false, // points to 'Border'
39235     
39236     
39237     createBody : function(){
39238         /** This region's body element 
39239         * @type Roo.Element */
39240         this.bodyEl = this.el.createChild({
39241                 tag: "div",
39242                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39243         });
39244     },
39245
39246     onRender: function(ctr, pos)
39247     {
39248         var dh = Roo.DomHelper;
39249         /** This region's container element 
39250         * @type Roo.Element */
39251         this.el = dh.append(ctr.dom, {
39252                 tag: "div",
39253                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39254             }, true);
39255         /** This region's title element 
39256         * @type Roo.Element */
39257     
39258         this.titleEl = dh.append(this.el.dom,  {
39259                 tag: "div",
39260                 unselectable: "on",
39261                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39262                 children:[
39263                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39264                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39265                 ]
39266             }, true);
39267         
39268         this.titleEl.enableDisplayMode();
39269         /** This region's title text element 
39270         * @type HTMLElement */
39271         this.titleTextEl = this.titleEl.dom.firstChild;
39272         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39273         /*
39274         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39275         this.closeBtn.enableDisplayMode();
39276         this.closeBtn.on("click", this.closeClicked, this);
39277         this.closeBtn.hide();
39278     */
39279         this.createBody(this.config);
39280         if(this.config.hideWhenEmpty){
39281             this.hide();
39282             this.on("paneladded", this.validateVisibility, this);
39283             this.on("panelremoved", this.validateVisibility, this);
39284         }
39285         if(this.autoScroll){
39286             this.bodyEl.setStyle("overflow", "auto");
39287         }else{
39288             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39289         }
39290         //if(c.titlebar !== false){
39291             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39292                 this.titleEl.hide();
39293             }else{
39294                 this.titleEl.show();
39295                 if(this.config.title){
39296                     this.titleTextEl.innerHTML = this.config.title;
39297                 }
39298             }
39299         //}
39300         if(this.config.collapsed){
39301             this.collapse(true);
39302         }
39303         if(this.config.hidden){
39304             this.hide();
39305         }
39306         
39307         if (this.unrendered_panels && this.unrendered_panels.length) {
39308             for (var i =0;i< this.unrendered_panels.length; i++) {
39309                 this.add(this.unrendered_panels[i]);
39310             }
39311             this.unrendered_panels = null;
39312             
39313         }
39314         
39315     },
39316     
39317     applyConfig : function(c)
39318     {
39319         /*
39320          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39321             var dh = Roo.DomHelper;
39322             if(c.titlebar !== false){
39323                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39324                 this.collapseBtn.on("click", this.collapse, this);
39325                 this.collapseBtn.enableDisplayMode();
39326                 /*
39327                 if(c.showPin === true || this.showPin){
39328                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39329                     this.stickBtn.enableDisplayMode();
39330                     this.stickBtn.on("click", this.expand, this);
39331                     this.stickBtn.hide();
39332                 }
39333                 
39334             }
39335             */
39336             /** This region's collapsed element
39337             * @type Roo.Element */
39338             /*
39339              *
39340             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39341                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39342             ]}, true);
39343             
39344             if(c.floatable !== false){
39345                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39346                this.collapsedEl.on("click", this.collapseClick, this);
39347             }
39348
39349             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39350                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39351                    id: "message", unselectable: "on", style:{"float":"left"}});
39352                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39353              }
39354             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39355             this.expandBtn.on("click", this.expand, this);
39356             
39357         }
39358         
39359         if(this.collapseBtn){
39360             this.collapseBtn.setVisible(c.collapsible == true);
39361         }
39362         
39363         this.cmargins = c.cmargins || this.cmargins ||
39364                          (this.position == "west" || this.position == "east" ?
39365                              {top: 0, left: 2, right:2, bottom: 0} :
39366                              {top: 2, left: 0, right:0, bottom: 2});
39367         */
39368         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39369         
39370         
39371         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39372         
39373         this.autoScroll = c.autoScroll || false;
39374         
39375         
39376        
39377         
39378         this.duration = c.duration || .30;
39379         this.slideDuration = c.slideDuration || .45;
39380         this.config = c;
39381        
39382     },
39383     /**
39384      * Returns true if this region is currently visible.
39385      * @return {Boolean}
39386      */
39387     isVisible : function(){
39388         return this.visible;
39389     },
39390
39391     /**
39392      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39393      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39394      */
39395     //setCollapsedTitle : function(title){
39396     //    title = title || "&#160;";
39397      //   if(this.collapsedTitleTextEl){
39398       //      this.collapsedTitleTextEl.innerHTML = title;
39399        // }
39400     //},
39401
39402     getBox : function(){
39403         var b;
39404       //  if(!this.collapsed){
39405             b = this.el.getBox(false, true);
39406        // }else{
39407           //  b = this.collapsedEl.getBox(false, true);
39408         //}
39409         return b;
39410     },
39411
39412     getMargins : function(){
39413         return this.margins;
39414         //return this.collapsed ? this.cmargins : this.margins;
39415     },
39416 /*
39417     highlight : function(){
39418         this.el.addClass("x-layout-panel-dragover");
39419     },
39420
39421     unhighlight : function(){
39422         this.el.removeClass("x-layout-panel-dragover");
39423     },
39424 */
39425     updateBox : function(box)
39426     {
39427         if (!this.bodyEl) {
39428             return; // not rendered yet..
39429         }
39430         
39431         this.box = box;
39432         if(!this.collapsed){
39433             this.el.dom.style.left = box.x + "px";
39434             this.el.dom.style.top = box.y + "px";
39435             this.updateBody(box.width, box.height);
39436         }else{
39437             this.collapsedEl.dom.style.left = box.x + "px";
39438             this.collapsedEl.dom.style.top = box.y + "px";
39439             this.collapsedEl.setSize(box.width, box.height);
39440         }
39441         if(this.tabs){
39442             this.tabs.autoSizeTabs();
39443         }
39444     },
39445
39446     updateBody : function(w, h)
39447     {
39448         if(w !== null){
39449             this.el.setWidth(w);
39450             w -= this.el.getBorderWidth("rl");
39451             if(this.config.adjustments){
39452                 w += this.config.adjustments[0];
39453             }
39454         }
39455         if(h !== null && h > 0){
39456             this.el.setHeight(h);
39457             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39458             h -= this.el.getBorderWidth("tb");
39459             if(this.config.adjustments){
39460                 h += this.config.adjustments[1];
39461             }
39462             this.bodyEl.setHeight(h);
39463             if(this.tabs){
39464                 h = this.tabs.syncHeight(h);
39465             }
39466         }
39467         if(this.panelSize){
39468             w = w !== null ? w : this.panelSize.width;
39469             h = h !== null ? h : this.panelSize.height;
39470         }
39471         if(this.activePanel){
39472             var el = this.activePanel.getEl();
39473             w = w !== null ? w : el.getWidth();
39474             h = h !== null ? h : el.getHeight();
39475             this.panelSize = {width: w, height: h};
39476             this.activePanel.setSize(w, h);
39477         }
39478         if(Roo.isIE && this.tabs){
39479             this.tabs.el.repaint();
39480         }
39481     },
39482
39483     /**
39484      * Returns the container element for this region.
39485      * @return {Roo.Element}
39486      */
39487     getEl : function(){
39488         return this.el;
39489     },
39490
39491     /**
39492      * Hides this region.
39493      */
39494     hide : function(){
39495         //if(!this.collapsed){
39496             this.el.dom.style.left = "-2000px";
39497             this.el.hide();
39498         //}else{
39499          //   this.collapsedEl.dom.style.left = "-2000px";
39500          //   this.collapsedEl.hide();
39501        // }
39502         this.visible = false;
39503         this.fireEvent("visibilitychange", this, false);
39504     },
39505
39506     /**
39507      * Shows this region if it was previously hidden.
39508      */
39509     show : function(){
39510         //if(!this.collapsed){
39511             this.el.show();
39512         //}else{
39513         //    this.collapsedEl.show();
39514        // }
39515         this.visible = true;
39516         this.fireEvent("visibilitychange", this, true);
39517     },
39518 /*
39519     closeClicked : function(){
39520         if(this.activePanel){
39521             this.remove(this.activePanel);
39522         }
39523     },
39524
39525     collapseClick : function(e){
39526         if(this.isSlid){
39527            e.stopPropagation();
39528            this.slideIn();
39529         }else{
39530            e.stopPropagation();
39531            this.slideOut();
39532         }
39533     },
39534 */
39535     /**
39536      * Collapses this region.
39537      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39538      */
39539     /*
39540     collapse : function(skipAnim, skipCheck = false){
39541         if(this.collapsed) {
39542             return;
39543         }
39544         
39545         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39546             
39547             this.collapsed = true;
39548             if(this.split){
39549                 this.split.el.hide();
39550             }
39551             if(this.config.animate && skipAnim !== true){
39552                 this.fireEvent("invalidated", this);
39553                 this.animateCollapse();
39554             }else{
39555                 this.el.setLocation(-20000,-20000);
39556                 this.el.hide();
39557                 this.collapsedEl.show();
39558                 this.fireEvent("collapsed", this);
39559                 this.fireEvent("invalidated", this);
39560             }
39561         }
39562         
39563     },
39564 */
39565     animateCollapse : function(){
39566         // overridden
39567     },
39568
39569     /**
39570      * Expands this region if it was previously collapsed.
39571      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39572      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39573      */
39574     /*
39575     expand : function(e, skipAnim){
39576         if(e) {
39577             e.stopPropagation();
39578         }
39579         if(!this.collapsed || this.el.hasActiveFx()) {
39580             return;
39581         }
39582         if(this.isSlid){
39583             this.afterSlideIn();
39584             skipAnim = true;
39585         }
39586         this.collapsed = false;
39587         if(this.config.animate && skipAnim !== true){
39588             this.animateExpand();
39589         }else{
39590             this.el.show();
39591             if(this.split){
39592                 this.split.el.show();
39593             }
39594             this.collapsedEl.setLocation(-2000,-2000);
39595             this.collapsedEl.hide();
39596             this.fireEvent("invalidated", this);
39597             this.fireEvent("expanded", this);
39598         }
39599     },
39600 */
39601     animateExpand : function(){
39602         // overridden
39603     },
39604
39605     initTabs : function()
39606     {
39607         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39608         
39609         var ts = new Roo.bootstrap.panel.Tabs({
39610             el: this.bodyEl.dom,
39611             region : this,
39612             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39613             disableTooltips: this.config.disableTabTips,
39614             toolbar : this.config.toolbar
39615         });
39616         
39617         if(this.config.hideTabs){
39618             ts.stripWrap.setDisplayed(false);
39619         }
39620         this.tabs = ts;
39621         ts.resizeTabs = this.config.resizeTabs === true;
39622         ts.minTabWidth = this.config.minTabWidth || 40;
39623         ts.maxTabWidth = this.config.maxTabWidth || 250;
39624         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39625         ts.monitorResize = false;
39626         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39627         ts.bodyEl.addClass('roo-layout-tabs-body');
39628         this.panels.each(this.initPanelAsTab, this);
39629     },
39630
39631     initPanelAsTab : function(panel){
39632         var ti = this.tabs.addTab(
39633             panel.getEl().id,
39634             panel.getTitle(),
39635             null,
39636             this.config.closeOnTab && panel.isClosable(),
39637             panel.tpl
39638         );
39639         if(panel.tabTip !== undefined){
39640             ti.setTooltip(panel.tabTip);
39641         }
39642         ti.on("activate", function(){
39643               this.setActivePanel(panel);
39644         }, this);
39645         
39646         if(this.config.closeOnTab){
39647             ti.on("beforeclose", function(t, e){
39648                 e.cancel = true;
39649                 this.remove(panel);
39650             }, this);
39651         }
39652         
39653         panel.tabItem = ti;
39654         
39655         return ti;
39656     },
39657
39658     updatePanelTitle : function(panel, title)
39659     {
39660         if(this.activePanel == panel){
39661             this.updateTitle(title);
39662         }
39663         if(this.tabs){
39664             var ti = this.tabs.getTab(panel.getEl().id);
39665             ti.setText(title);
39666             if(panel.tabTip !== undefined){
39667                 ti.setTooltip(panel.tabTip);
39668             }
39669         }
39670     },
39671
39672     updateTitle : function(title){
39673         if(this.titleTextEl && !this.config.title){
39674             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39675         }
39676     },
39677
39678     setActivePanel : function(panel)
39679     {
39680         panel = this.getPanel(panel);
39681         if(this.activePanel && this.activePanel != panel){
39682             if(this.activePanel.setActiveState(false) === false){
39683                 return;
39684             }
39685         }
39686         this.activePanel = panel;
39687         panel.setActiveState(true);
39688         if(this.panelSize){
39689             panel.setSize(this.panelSize.width, this.panelSize.height);
39690         }
39691         if(this.closeBtn){
39692             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39693         }
39694         this.updateTitle(panel.getTitle());
39695         if(this.tabs){
39696             this.fireEvent("invalidated", this);
39697         }
39698         this.fireEvent("panelactivated", this, panel);
39699     },
39700
39701     /**
39702      * Shows the specified panel.
39703      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39704      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39705      */
39706     showPanel : function(panel)
39707     {
39708         panel = this.getPanel(panel);
39709         if(panel){
39710             if(this.tabs){
39711                 var tab = this.tabs.getTab(panel.getEl().id);
39712                 if(tab.isHidden()){
39713                     this.tabs.unhideTab(tab.id);
39714                 }
39715                 tab.activate();
39716             }else{
39717                 this.setActivePanel(panel);
39718             }
39719         }
39720         return panel;
39721     },
39722
39723     /**
39724      * Get the active panel for this region.
39725      * @return {Roo.ContentPanel} The active panel or null
39726      */
39727     getActivePanel : function(){
39728         return this.activePanel;
39729     },
39730
39731     validateVisibility : function(){
39732         if(this.panels.getCount() < 1){
39733             this.updateTitle("&#160;");
39734             this.closeBtn.hide();
39735             this.hide();
39736         }else{
39737             if(!this.isVisible()){
39738                 this.show();
39739             }
39740         }
39741     },
39742
39743     /**
39744      * Adds the passed ContentPanel(s) to this region.
39745      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39746      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39747      */
39748     add : function(panel)
39749     {
39750         if(arguments.length > 1){
39751             for(var i = 0, len = arguments.length; i < len; i++) {
39752                 this.add(arguments[i]);
39753             }
39754             return null;
39755         }
39756         
39757         // if we have not been rendered yet, then we can not really do much of this..
39758         if (!this.bodyEl) {
39759             this.unrendered_panels.push(panel);
39760             return panel;
39761         }
39762         
39763         
39764         
39765         
39766         if(this.hasPanel(panel)){
39767             this.showPanel(panel);
39768             return panel;
39769         }
39770         panel.setRegion(this);
39771         this.panels.add(panel);
39772        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39773             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39774             // and hide them... ???
39775             this.bodyEl.dom.appendChild(panel.getEl().dom);
39776             if(panel.background !== true){
39777                 this.setActivePanel(panel);
39778             }
39779             this.fireEvent("paneladded", this, panel);
39780             return panel;
39781         }
39782         */
39783         if(!this.tabs){
39784             this.initTabs();
39785         }else{
39786             this.initPanelAsTab(panel);
39787         }
39788         
39789         
39790         if(panel.background !== true){
39791             this.tabs.activate(panel.getEl().id);
39792         }
39793         this.fireEvent("paneladded", this, panel);
39794         return panel;
39795     },
39796
39797     /**
39798      * Hides the tab for the specified panel.
39799      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39800      */
39801     hidePanel : function(panel){
39802         if(this.tabs && (panel = this.getPanel(panel))){
39803             this.tabs.hideTab(panel.getEl().id);
39804         }
39805     },
39806
39807     /**
39808      * Unhides the tab for a previously hidden panel.
39809      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39810      */
39811     unhidePanel : function(panel){
39812         if(this.tabs && (panel = this.getPanel(panel))){
39813             this.tabs.unhideTab(panel.getEl().id);
39814         }
39815     },
39816
39817     clearPanels : function(){
39818         while(this.panels.getCount() > 0){
39819              this.remove(this.panels.first());
39820         }
39821     },
39822
39823     /**
39824      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39825      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39826      * @param {Boolean} preservePanel Overrides the config preservePanel option
39827      * @return {Roo.ContentPanel} The panel that was removed
39828      */
39829     remove : function(panel, preservePanel)
39830     {
39831         panel = this.getPanel(panel);
39832         if(!panel){
39833             return null;
39834         }
39835         var e = {};
39836         this.fireEvent("beforeremove", this, panel, e);
39837         if(e.cancel === true){
39838             return null;
39839         }
39840         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39841         var panelId = panel.getId();
39842         this.panels.removeKey(panelId);
39843         if(preservePanel){
39844             document.body.appendChild(panel.getEl().dom);
39845         }
39846         if(this.tabs){
39847             this.tabs.removeTab(panel.getEl().id);
39848         }else if (!preservePanel){
39849             this.bodyEl.dom.removeChild(panel.getEl().dom);
39850         }
39851         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39852             var p = this.panels.first();
39853             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39854             tempEl.appendChild(p.getEl().dom);
39855             this.bodyEl.update("");
39856             this.bodyEl.dom.appendChild(p.getEl().dom);
39857             tempEl = null;
39858             this.updateTitle(p.getTitle());
39859             this.tabs = null;
39860             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39861             this.setActivePanel(p);
39862         }
39863         panel.setRegion(null);
39864         if(this.activePanel == panel){
39865             this.activePanel = null;
39866         }
39867         if(this.config.autoDestroy !== false && preservePanel !== true){
39868             try{panel.destroy();}catch(e){}
39869         }
39870         this.fireEvent("panelremoved", this, panel);
39871         return panel;
39872     },
39873
39874     /**
39875      * Returns the TabPanel component used by this region
39876      * @return {Roo.TabPanel}
39877      */
39878     getTabs : function(){
39879         return this.tabs;
39880     },
39881
39882     createTool : function(parentEl, className){
39883         var btn = Roo.DomHelper.append(parentEl, {
39884             tag: "div",
39885             cls: "x-layout-tools-button",
39886             children: [ {
39887                 tag: "div",
39888                 cls: "roo-layout-tools-button-inner " + className,
39889                 html: "&#160;"
39890             }]
39891         }, true);
39892         btn.addClassOnOver("roo-layout-tools-button-over");
39893         return btn;
39894     }
39895 });/*
39896  * Based on:
39897  * Ext JS Library 1.1.1
39898  * Copyright(c) 2006-2007, Ext JS, LLC.
39899  *
39900  * Originally Released Under LGPL - original licence link has changed is not relivant.
39901  *
39902  * Fork - LGPL
39903  * <script type="text/javascript">
39904  */
39905  
39906
39907
39908 /**
39909  * @class Roo.SplitLayoutRegion
39910  * @extends Roo.LayoutRegion
39911  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39912  */
39913 Roo.bootstrap.layout.Split = function(config){
39914     this.cursor = config.cursor;
39915     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39916 };
39917
39918 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39919 {
39920     splitTip : "Drag to resize.",
39921     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39922     useSplitTips : false,
39923
39924     applyConfig : function(config){
39925         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39926     },
39927     
39928     onRender : function(ctr,pos) {
39929         
39930         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39931         if(!this.config.split){
39932             return;
39933         }
39934         if(!this.split){
39935             
39936             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39937                             tag: "div",
39938                             id: this.el.id + "-split",
39939                             cls: "roo-layout-split roo-layout-split-"+this.position,
39940                             html: "&#160;"
39941             });
39942             /** The SplitBar for this region 
39943             * @type Roo.SplitBar */
39944             // does not exist yet...
39945             Roo.log([this.position, this.orientation]);
39946             
39947             this.split = new Roo.bootstrap.SplitBar({
39948                 dragElement : splitEl,
39949                 resizingElement: this.el,
39950                 orientation : this.orientation
39951             });
39952             
39953             this.split.on("moved", this.onSplitMove, this);
39954             this.split.useShim = this.config.useShim === true;
39955             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39956             if(this.useSplitTips){
39957                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39958             }
39959             //if(config.collapsible){
39960             //    this.split.el.on("dblclick", this.collapse,  this);
39961             //}
39962         }
39963         if(typeof this.config.minSize != "undefined"){
39964             this.split.minSize = this.config.minSize;
39965         }
39966         if(typeof this.config.maxSize != "undefined"){
39967             this.split.maxSize = this.config.maxSize;
39968         }
39969         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39970             this.hideSplitter();
39971         }
39972         
39973     },
39974
39975     getHMaxSize : function(){
39976          var cmax = this.config.maxSize || 10000;
39977          var center = this.mgr.getRegion("center");
39978          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39979     },
39980
39981     getVMaxSize : function(){
39982          var cmax = this.config.maxSize || 10000;
39983          var center = this.mgr.getRegion("center");
39984          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39985     },
39986
39987     onSplitMove : function(split, newSize){
39988         this.fireEvent("resized", this, newSize);
39989     },
39990     
39991     /** 
39992      * Returns the {@link Roo.SplitBar} for this region.
39993      * @return {Roo.SplitBar}
39994      */
39995     getSplitBar : function(){
39996         return this.split;
39997     },
39998     
39999     hide : function(){
40000         this.hideSplitter();
40001         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40002     },
40003
40004     hideSplitter : function(){
40005         if(this.split){
40006             this.split.el.setLocation(-2000,-2000);
40007             this.split.el.hide();
40008         }
40009     },
40010
40011     show : function(){
40012         if(this.split){
40013             this.split.el.show();
40014         }
40015         Roo.bootstrap.layout.Split.superclass.show.call(this);
40016     },
40017     
40018     beforeSlide: function(){
40019         if(Roo.isGecko){// firefox overflow auto bug workaround
40020             this.bodyEl.clip();
40021             if(this.tabs) {
40022                 this.tabs.bodyEl.clip();
40023             }
40024             if(this.activePanel){
40025                 this.activePanel.getEl().clip();
40026                 
40027                 if(this.activePanel.beforeSlide){
40028                     this.activePanel.beforeSlide();
40029                 }
40030             }
40031         }
40032     },
40033     
40034     afterSlide : function(){
40035         if(Roo.isGecko){// firefox overflow auto bug workaround
40036             this.bodyEl.unclip();
40037             if(this.tabs) {
40038                 this.tabs.bodyEl.unclip();
40039             }
40040             if(this.activePanel){
40041                 this.activePanel.getEl().unclip();
40042                 if(this.activePanel.afterSlide){
40043                     this.activePanel.afterSlide();
40044                 }
40045             }
40046         }
40047     },
40048
40049     initAutoHide : function(){
40050         if(this.autoHide !== false){
40051             if(!this.autoHideHd){
40052                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40053                 this.autoHideHd = {
40054                     "mouseout": function(e){
40055                         if(!e.within(this.el, true)){
40056                             st.delay(500);
40057                         }
40058                     },
40059                     "mouseover" : function(e){
40060                         st.cancel();
40061                     },
40062                     scope : this
40063                 };
40064             }
40065             this.el.on(this.autoHideHd);
40066         }
40067     },
40068
40069     clearAutoHide : function(){
40070         if(this.autoHide !== false){
40071             this.el.un("mouseout", this.autoHideHd.mouseout);
40072             this.el.un("mouseover", this.autoHideHd.mouseover);
40073         }
40074     },
40075
40076     clearMonitor : function(){
40077         Roo.get(document).un("click", this.slideInIf, this);
40078     },
40079
40080     // these names are backwards but not changed for compat
40081     slideOut : function(){
40082         if(this.isSlid || this.el.hasActiveFx()){
40083             return;
40084         }
40085         this.isSlid = true;
40086         if(this.collapseBtn){
40087             this.collapseBtn.hide();
40088         }
40089         this.closeBtnState = this.closeBtn.getStyle('display');
40090         this.closeBtn.hide();
40091         if(this.stickBtn){
40092             this.stickBtn.show();
40093         }
40094         this.el.show();
40095         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40096         this.beforeSlide();
40097         this.el.setStyle("z-index", 10001);
40098         this.el.slideIn(this.getSlideAnchor(), {
40099             callback: function(){
40100                 this.afterSlide();
40101                 this.initAutoHide();
40102                 Roo.get(document).on("click", this.slideInIf, this);
40103                 this.fireEvent("slideshow", this);
40104             },
40105             scope: this,
40106             block: true
40107         });
40108     },
40109
40110     afterSlideIn : function(){
40111         this.clearAutoHide();
40112         this.isSlid = false;
40113         this.clearMonitor();
40114         this.el.setStyle("z-index", "");
40115         if(this.collapseBtn){
40116             this.collapseBtn.show();
40117         }
40118         this.closeBtn.setStyle('display', this.closeBtnState);
40119         if(this.stickBtn){
40120             this.stickBtn.hide();
40121         }
40122         this.fireEvent("slidehide", this);
40123     },
40124
40125     slideIn : function(cb){
40126         if(!this.isSlid || this.el.hasActiveFx()){
40127             Roo.callback(cb);
40128             return;
40129         }
40130         this.isSlid = false;
40131         this.beforeSlide();
40132         this.el.slideOut(this.getSlideAnchor(), {
40133             callback: function(){
40134                 this.el.setLeftTop(-10000, -10000);
40135                 this.afterSlide();
40136                 this.afterSlideIn();
40137                 Roo.callback(cb);
40138             },
40139             scope: this,
40140             block: true
40141         });
40142     },
40143     
40144     slideInIf : function(e){
40145         if(!e.within(this.el)){
40146             this.slideIn();
40147         }
40148     },
40149
40150     animateCollapse : function(){
40151         this.beforeSlide();
40152         this.el.setStyle("z-index", 20000);
40153         var anchor = this.getSlideAnchor();
40154         this.el.slideOut(anchor, {
40155             callback : function(){
40156                 this.el.setStyle("z-index", "");
40157                 this.collapsedEl.slideIn(anchor, {duration:.3});
40158                 this.afterSlide();
40159                 this.el.setLocation(-10000,-10000);
40160                 this.el.hide();
40161                 this.fireEvent("collapsed", this);
40162             },
40163             scope: this,
40164             block: true
40165         });
40166     },
40167
40168     animateExpand : function(){
40169         this.beforeSlide();
40170         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40171         this.el.setStyle("z-index", 20000);
40172         this.collapsedEl.hide({
40173             duration:.1
40174         });
40175         this.el.slideIn(this.getSlideAnchor(), {
40176             callback : function(){
40177                 this.el.setStyle("z-index", "");
40178                 this.afterSlide();
40179                 if(this.split){
40180                     this.split.el.show();
40181                 }
40182                 this.fireEvent("invalidated", this);
40183                 this.fireEvent("expanded", this);
40184             },
40185             scope: this,
40186             block: true
40187         });
40188     },
40189
40190     anchors : {
40191         "west" : "left",
40192         "east" : "right",
40193         "north" : "top",
40194         "south" : "bottom"
40195     },
40196
40197     sanchors : {
40198         "west" : "l",
40199         "east" : "r",
40200         "north" : "t",
40201         "south" : "b"
40202     },
40203
40204     canchors : {
40205         "west" : "tl-tr",
40206         "east" : "tr-tl",
40207         "north" : "tl-bl",
40208         "south" : "bl-tl"
40209     },
40210
40211     getAnchor : function(){
40212         return this.anchors[this.position];
40213     },
40214
40215     getCollapseAnchor : function(){
40216         return this.canchors[this.position];
40217     },
40218
40219     getSlideAnchor : function(){
40220         return this.sanchors[this.position];
40221     },
40222
40223     getAlignAdj : function(){
40224         var cm = this.cmargins;
40225         switch(this.position){
40226             case "west":
40227                 return [0, 0];
40228             break;
40229             case "east":
40230                 return [0, 0];
40231             break;
40232             case "north":
40233                 return [0, 0];
40234             break;
40235             case "south":
40236                 return [0, 0];
40237             break;
40238         }
40239     },
40240
40241     getExpandAdj : function(){
40242         var c = this.collapsedEl, cm = this.cmargins;
40243         switch(this.position){
40244             case "west":
40245                 return [-(cm.right+c.getWidth()+cm.left), 0];
40246             break;
40247             case "east":
40248                 return [cm.right+c.getWidth()+cm.left, 0];
40249             break;
40250             case "north":
40251                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40252             break;
40253             case "south":
40254                 return [0, cm.top+cm.bottom+c.getHeight()];
40255             break;
40256         }
40257     }
40258 });/*
40259  * Based on:
40260  * Ext JS Library 1.1.1
40261  * Copyright(c) 2006-2007, Ext JS, LLC.
40262  *
40263  * Originally Released Under LGPL - original licence link has changed is not relivant.
40264  *
40265  * Fork - LGPL
40266  * <script type="text/javascript">
40267  */
40268 /*
40269  * These classes are private internal classes
40270  */
40271 Roo.bootstrap.layout.Center = function(config){
40272     config.region = "center";
40273     Roo.bootstrap.layout.Region.call(this, config);
40274     this.visible = true;
40275     this.minWidth = config.minWidth || 20;
40276     this.minHeight = config.minHeight || 20;
40277 };
40278
40279 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40280     hide : function(){
40281         // center panel can't be hidden
40282     },
40283     
40284     show : function(){
40285         // center panel can't be hidden
40286     },
40287     
40288     getMinWidth: function(){
40289         return this.minWidth;
40290     },
40291     
40292     getMinHeight: function(){
40293         return this.minHeight;
40294     }
40295 });
40296
40297
40298
40299
40300  
40301
40302
40303
40304
40305
40306
40307 Roo.bootstrap.layout.North = function(config)
40308 {
40309     config.region = 'north';
40310     config.cursor = 'n-resize';
40311     
40312     Roo.bootstrap.layout.Split.call(this, config);
40313     
40314     
40315     if(this.split){
40316         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40317         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40318         this.split.el.addClass("roo-layout-split-v");
40319     }
40320     //var size = config.initialSize || config.height;
40321     //if(this.el && typeof size != "undefined"){
40322     //    this.el.setHeight(size);
40323     //}
40324 };
40325 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40326 {
40327     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40328      
40329      
40330     onRender : function(ctr, pos)
40331     {
40332         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40333         var size = this.config.initialSize || this.config.height;
40334         if(this.el && typeof size != "undefined"){
40335             this.el.setHeight(size);
40336         }
40337     
40338     },
40339     
40340     getBox : function(){
40341         if(this.collapsed){
40342             return this.collapsedEl.getBox();
40343         }
40344         var box = this.el.getBox();
40345         if(this.split){
40346             box.height += this.split.el.getHeight();
40347         }
40348         return box;
40349     },
40350     
40351     updateBox : function(box){
40352         if(this.split && !this.collapsed){
40353             box.height -= this.split.el.getHeight();
40354             this.split.el.setLeft(box.x);
40355             this.split.el.setTop(box.y+box.height);
40356             this.split.el.setWidth(box.width);
40357         }
40358         if(this.collapsed){
40359             this.updateBody(box.width, null);
40360         }
40361         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40362     }
40363 });
40364
40365
40366
40367
40368
40369 Roo.bootstrap.layout.South = function(config){
40370     config.region = 'south';
40371     config.cursor = 's-resize';
40372     Roo.bootstrap.layout.Split.call(this, config);
40373     if(this.split){
40374         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40375         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40376         this.split.el.addClass("roo-layout-split-v");
40377     }
40378     
40379 };
40380
40381 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40382     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40383     
40384     onRender : function(ctr, pos)
40385     {
40386         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40387         var size = this.config.initialSize || this.config.height;
40388         if(this.el && typeof size != "undefined"){
40389             this.el.setHeight(size);
40390         }
40391     
40392     },
40393     
40394     getBox : function(){
40395         if(this.collapsed){
40396             return this.collapsedEl.getBox();
40397         }
40398         var box = this.el.getBox();
40399         if(this.split){
40400             var sh = this.split.el.getHeight();
40401             box.height += sh;
40402             box.y -= sh;
40403         }
40404         return box;
40405     },
40406     
40407     updateBox : function(box){
40408         if(this.split && !this.collapsed){
40409             var sh = this.split.el.getHeight();
40410             box.height -= sh;
40411             box.y += sh;
40412             this.split.el.setLeft(box.x);
40413             this.split.el.setTop(box.y-sh);
40414             this.split.el.setWidth(box.width);
40415         }
40416         if(this.collapsed){
40417             this.updateBody(box.width, null);
40418         }
40419         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40420     }
40421 });
40422
40423 Roo.bootstrap.layout.East = function(config){
40424     config.region = "east";
40425     config.cursor = "e-resize";
40426     Roo.bootstrap.layout.Split.call(this, config);
40427     if(this.split){
40428         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40429         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40430         this.split.el.addClass("roo-layout-split-h");
40431     }
40432     
40433 };
40434 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40435     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40436     
40437     onRender : function(ctr, pos)
40438     {
40439         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40440         var size = this.config.initialSize || this.config.width;
40441         if(this.el && typeof size != "undefined"){
40442             this.el.setWidth(size);
40443         }
40444     
40445     },
40446     
40447     getBox : function(){
40448         if(this.collapsed){
40449             return this.collapsedEl.getBox();
40450         }
40451         var box = this.el.getBox();
40452         if(this.split){
40453             var sw = this.split.el.getWidth();
40454             box.width += sw;
40455             box.x -= sw;
40456         }
40457         return box;
40458     },
40459
40460     updateBox : function(box){
40461         if(this.split && !this.collapsed){
40462             var sw = this.split.el.getWidth();
40463             box.width -= sw;
40464             this.split.el.setLeft(box.x);
40465             this.split.el.setTop(box.y);
40466             this.split.el.setHeight(box.height);
40467             box.x += sw;
40468         }
40469         if(this.collapsed){
40470             this.updateBody(null, box.height);
40471         }
40472         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40473     }
40474 });
40475
40476 Roo.bootstrap.layout.West = function(config){
40477     config.region = "west";
40478     config.cursor = "w-resize";
40479     
40480     Roo.bootstrap.layout.Split.call(this, config);
40481     if(this.split){
40482         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40483         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40484         this.split.el.addClass("roo-layout-split-h");
40485     }
40486     
40487 };
40488 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40489     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40490     
40491     onRender: function(ctr, pos)
40492     {
40493         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40494         var size = this.config.initialSize || this.config.width;
40495         if(typeof size != "undefined"){
40496             this.el.setWidth(size);
40497         }
40498     },
40499     
40500     getBox : function(){
40501         if(this.collapsed){
40502             return this.collapsedEl.getBox();
40503         }
40504         var box = this.el.getBox();
40505         if (box.width == 0) {
40506             box.width = this.config.width; // kludge?
40507         }
40508         if(this.split){
40509             box.width += this.split.el.getWidth();
40510         }
40511         return box;
40512     },
40513     
40514     updateBox : function(box){
40515         if(this.split && !this.collapsed){
40516             var sw = this.split.el.getWidth();
40517             box.width -= sw;
40518             this.split.el.setLeft(box.x+box.width);
40519             this.split.el.setTop(box.y);
40520             this.split.el.setHeight(box.height);
40521         }
40522         if(this.collapsed){
40523             this.updateBody(null, box.height);
40524         }
40525         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40526     }
40527 });Roo.namespace("Roo.bootstrap.panel");/*
40528  * Based on:
40529  * Ext JS Library 1.1.1
40530  * Copyright(c) 2006-2007, Ext JS, LLC.
40531  *
40532  * Originally Released Under LGPL - original licence link has changed is not relivant.
40533  *
40534  * Fork - LGPL
40535  * <script type="text/javascript">
40536  */
40537 /**
40538  * @class Roo.ContentPanel
40539  * @extends Roo.util.Observable
40540  * A basic ContentPanel element.
40541  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40542  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40543  * @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
40544  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40545  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40546  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40547  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40548  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40549  * @cfg {String} title          The title for this panel
40550  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40551  * @cfg {String} url            Calls {@link #setUrl} with this value
40552  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40553  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40554  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40555  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40556  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40557  * @cfg {Boolean} badges render the badges
40558  * @cfg {String} cls  extra classes to use  
40559  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40560
40561  * @constructor
40562  * Create a new ContentPanel.
40563  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40564  * @param {String/Object} config A string to set only the title or a config object
40565  * @param {String} content (optional) Set the HTML content for this panel
40566  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40567  */
40568 Roo.bootstrap.panel.Content = function( config){
40569     
40570     this.tpl = config.tpl || false;
40571     
40572     var el = config.el;
40573     var content = config.content;
40574
40575     if(config.autoCreate){ // xtype is available if this is called from factory
40576         el = Roo.id();
40577     }
40578     this.el = Roo.get(el);
40579     if(!this.el && config && config.autoCreate){
40580         if(typeof config.autoCreate == "object"){
40581             if(!config.autoCreate.id){
40582                 config.autoCreate.id = config.id||el;
40583             }
40584             this.el = Roo.DomHelper.append(document.body,
40585                         config.autoCreate, true);
40586         }else{
40587             var elcfg =  {
40588                 tag: "div",
40589                 cls: (config.cls || '') +
40590                     (config.background ? ' bg-' + config.background : '') +
40591                     " roo-layout-inactive-content",
40592                 id: config.id||el
40593             };
40594             if (config.iframe) {
40595                 elcfg.cn = [
40596                     {
40597                         tag : 'iframe',
40598                         style : 'border: 0px',
40599                         src : 'about:blank'
40600                     }
40601                 ];
40602             }
40603               
40604             if (config.html) {
40605                 elcfg.html = config.html;
40606                 
40607             }
40608                         
40609             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40610             if (config.iframe) {
40611                 this.iframeEl = this.el.select('iframe',true).first();
40612             }
40613             
40614         }
40615     } 
40616     this.closable = false;
40617     this.loaded = false;
40618     this.active = false;
40619    
40620       
40621     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40622         
40623         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40624         
40625         this.wrapEl = this.el; //this.el.wrap();
40626         var ti = [];
40627         if (config.toolbar.items) {
40628             ti = config.toolbar.items ;
40629             delete config.toolbar.items ;
40630         }
40631         
40632         var nitems = [];
40633         this.toolbar.render(this.wrapEl, 'before');
40634         for(var i =0;i < ti.length;i++) {
40635           //  Roo.log(['add child', items[i]]);
40636             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40637         }
40638         this.toolbar.items = nitems;
40639         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40640         delete config.toolbar;
40641         
40642     }
40643     /*
40644     // xtype created footer. - not sure if will work as we normally have to render first..
40645     if (this.footer && !this.footer.el && this.footer.xtype) {
40646         if (!this.wrapEl) {
40647             this.wrapEl = this.el.wrap();
40648         }
40649     
40650         this.footer.container = this.wrapEl.createChild();
40651          
40652         this.footer = Roo.factory(this.footer, Roo);
40653         
40654     }
40655     */
40656     
40657      if(typeof config == "string"){
40658         this.title = config;
40659     }else{
40660         Roo.apply(this, config);
40661     }
40662     
40663     if(this.resizeEl){
40664         this.resizeEl = Roo.get(this.resizeEl, true);
40665     }else{
40666         this.resizeEl = this.el;
40667     }
40668     // handle view.xtype
40669     
40670  
40671     
40672     
40673     this.addEvents({
40674         /**
40675          * @event activate
40676          * Fires when this panel is activated. 
40677          * @param {Roo.ContentPanel} this
40678          */
40679         "activate" : true,
40680         /**
40681          * @event deactivate
40682          * Fires when this panel is activated. 
40683          * @param {Roo.ContentPanel} this
40684          */
40685         "deactivate" : true,
40686
40687         /**
40688          * @event resize
40689          * Fires when this panel is resized if fitToFrame is true.
40690          * @param {Roo.ContentPanel} this
40691          * @param {Number} width The width after any component adjustments
40692          * @param {Number} height The height after any component adjustments
40693          */
40694         "resize" : true,
40695         
40696          /**
40697          * @event render
40698          * Fires when this tab is created
40699          * @param {Roo.ContentPanel} this
40700          */
40701         "render" : true,
40702         
40703           /**
40704          * @event scroll
40705          * Fires when this content is scrolled
40706          * @param {Roo.ContentPanel} this
40707          * @param {Event} scrollEvent
40708          */
40709         "scroll" : true
40710         
40711         
40712         
40713     });
40714     
40715
40716     
40717     
40718     if(this.autoScroll && !this.iframe){
40719         this.resizeEl.setStyle("overflow", "auto");
40720         this.resizeEl.on('scroll', this.onScroll, this);
40721     } else {
40722         // fix randome scrolling
40723         //this.el.on('scroll', function() {
40724         //    Roo.log('fix random scolling');
40725         //    this.scrollTo('top',0); 
40726         //});
40727     }
40728     content = content || this.content;
40729     if(content){
40730         this.setContent(content);
40731     }
40732     if(config && config.url){
40733         this.setUrl(this.url, this.params, this.loadOnce);
40734     }
40735     
40736     
40737     
40738     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40739     
40740     if (this.view && typeof(this.view.xtype) != 'undefined') {
40741         this.view.el = this.el.appendChild(document.createElement("div"));
40742         this.view = Roo.factory(this.view); 
40743         this.view.render  &&  this.view.render(false, '');  
40744     }
40745     
40746     
40747     this.fireEvent('render', this);
40748 };
40749
40750 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40751     
40752     cls : '',
40753     background : '',
40754     
40755     tabTip : '',
40756     
40757     iframe : false,
40758     iframeEl : false,
40759     
40760     /* Resize Element - use this to work out scroll etc. */
40761     resizeEl : false,
40762     
40763     setRegion : function(region){
40764         this.region = region;
40765         this.setActiveClass(region && !this.background);
40766     },
40767     
40768     
40769     setActiveClass: function(state)
40770     {
40771         if(state){
40772            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40773            this.el.setStyle('position','relative');
40774         }else{
40775            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40776            this.el.setStyle('position', 'absolute');
40777         } 
40778     },
40779     
40780     /**
40781      * Returns the toolbar for this Panel if one was configured. 
40782      * @return {Roo.Toolbar} 
40783      */
40784     getToolbar : function(){
40785         return this.toolbar;
40786     },
40787     
40788     setActiveState : function(active)
40789     {
40790         this.active = active;
40791         this.setActiveClass(active);
40792         if(!active){
40793             if(this.fireEvent("deactivate", this) === false){
40794                 return false;
40795             }
40796             return true;
40797         }
40798         this.fireEvent("activate", this);
40799         return true;
40800     },
40801     /**
40802      * Updates this panel's element (not for iframe)
40803      * @param {String} content The new content
40804      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40805     */
40806     setContent : function(content, loadScripts){
40807         if (this.iframe) {
40808             return;
40809         }
40810         
40811         this.el.update(content, loadScripts);
40812     },
40813
40814     ignoreResize : function(w, h){
40815         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40816             return true;
40817         }else{
40818             this.lastSize = {width: w, height: h};
40819             return false;
40820         }
40821     },
40822     /**
40823      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40824      * @return {Roo.UpdateManager} The UpdateManager
40825      */
40826     getUpdateManager : function(){
40827         if (this.iframe) {
40828             return false;
40829         }
40830         return this.el.getUpdateManager();
40831     },
40832      /**
40833      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40834      * Does not work with IFRAME contents
40835      * @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:
40836 <pre><code>
40837 panel.load({
40838     url: "your-url.php",
40839     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40840     callback: yourFunction,
40841     scope: yourObject, //(optional scope)
40842     discardUrl: false,
40843     nocache: false,
40844     text: "Loading...",
40845     timeout: 30,
40846     scripts: false
40847 });
40848 </code></pre>
40849      
40850      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40851      * 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.
40852      * @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}
40853      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40854      * @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.
40855      * @return {Roo.ContentPanel} this
40856      */
40857     load : function(){
40858         
40859         if (this.iframe) {
40860             return this;
40861         }
40862         
40863         var um = this.el.getUpdateManager();
40864         um.update.apply(um, arguments);
40865         return this;
40866     },
40867
40868
40869     /**
40870      * 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.
40871      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40872      * @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)
40873      * @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)
40874      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40875      */
40876     setUrl : function(url, params, loadOnce){
40877         if (this.iframe) {
40878             this.iframeEl.dom.src = url;
40879             return false;
40880         }
40881         
40882         if(this.refreshDelegate){
40883             this.removeListener("activate", this.refreshDelegate);
40884         }
40885         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40886         this.on("activate", this.refreshDelegate);
40887         return this.el.getUpdateManager();
40888     },
40889     
40890     _handleRefresh : function(url, params, loadOnce){
40891         if(!loadOnce || !this.loaded){
40892             var updater = this.el.getUpdateManager();
40893             updater.update(url, params, this._setLoaded.createDelegate(this));
40894         }
40895     },
40896     
40897     _setLoaded : function(){
40898         this.loaded = true;
40899     }, 
40900     
40901     /**
40902      * Returns this panel's id
40903      * @return {String} 
40904      */
40905     getId : function(){
40906         return this.el.id;
40907     },
40908     
40909     /** 
40910      * Returns this panel's element - used by regiosn to add.
40911      * @return {Roo.Element} 
40912      */
40913     getEl : function(){
40914         return this.wrapEl || this.el;
40915     },
40916     
40917    
40918     
40919     adjustForComponents : function(width, height)
40920     {
40921         //Roo.log('adjustForComponents ');
40922         if(this.resizeEl != this.el){
40923             width -= this.el.getFrameWidth('lr');
40924             height -= this.el.getFrameWidth('tb');
40925         }
40926         if(this.toolbar){
40927             var te = this.toolbar.getEl();
40928             te.setWidth(width);
40929             height -= te.getHeight();
40930         }
40931         if(this.footer){
40932             var te = this.footer.getEl();
40933             te.setWidth(width);
40934             height -= te.getHeight();
40935         }
40936         
40937         
40938         if(this.adjustments){
40939             width += this.adjustments[0];
40940             height += this.adjustments[1];
40941         }
40942         return {"width": width, "height": height};
40943     },
40944     
40945     setSize : function(width, height){
40946         if(this.fitToFrame && !this.ignoreResize(width, height)){
40947             if(this.fitContainer && this.resizeEl != this.el){
40948                 this.el.setSize(width, height);
40949             }
40950             var size = this.adjustForComponents(width, height);
40951             if (this.iframe) {
40952                 this.iframeEl.setSize(width,height);
40953             }
40954             
40955             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40956             this.fireEvent('resize', this, size.width, size.height);
40957             
40958             
40959         }
40960     },
40961     
40962     /**
40963      * Returns this panel's title
40964      * @return {String} 
40965      */
40966     getTitle : function(){
40967         
40968         if (typeof(this.title) != 'object') {
40969             return this.title;
40970         }
40971         
40972         var t = '';
40973         for (var k in this.title) {
40974             if (!this.title.hasOwnProperty(k)) {
40975                 continue;
40976             }
40977             
40978             if (k.indexOf('-') >= 0) {
40979                 var s = k.split('-');
40980                 for (var i = 0; i<s.length; i++) {
40981                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40982                 }
40983             } else {
40984                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40985             }
40986         }
40987         return t;
40988     },
40989     
40990     /**
40991      * Set this panel's title
40992      * @param {String} title
40993      */
40994     setTitle : function(title){
40995         this.title = title;
40996         if(this.region){
40997             this.region.updatePanelTitle(this, title);
40998         }
40999     },
41000     
41001     /**
41002      * Returns true is this panel was configured to be closable
41003      * @return {Boolean} 
41004      */
41005     isClosable : function(){
41006         return this.closable;
41007     },
41008     
41009     beforeSlide : function(){
41010         this.el.clip();
41011         this.resizeEl.clip();
41012     },
41013     
41014     afterSlide : function(){
41015         this.el.unclip();
41016         this.resizeEl.unclip();
41017     },
41018     
41019     /**
41020      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41021      *   Will fail silently if the {@link #setUrl} method has not been called.
41022      *   This does not activate the panel, just updates its content.
41023      */
41024     refresh : function(){
41025         if(this.refreshDelegate){
41026            this.loaded = false;
41027            this.refreshDelegate();
41028         }
41029     },
41030     
41031     /**
41032      * Destroys this panel
41033      */
41034     destroy : function(){
41035         this.el.removeAllListeners();
41036         var tempEl = document.createElement("span");
41037         tempEl.appendChild(this.el.dom);
41038         tempEl.innerHTML = "";
41039         this.el.remove();
41040         this.el = null;
41041     },
41042     
41043     /**
41044      * form - if the content panel contains a form - this is a reference to it.
41045      * @type {Roo.form.Form}
41046      */
41047     form : false,
41048     /**
41049      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41050      *    This contains a reference to it.
41051      * @type {Roo.View}
41052      */
41053     view : false,
41054     
41055       /**
41056      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41057      * <pre><code>
41058
41059 layout.addxtype({
41060        xtype : 'Form',
41061        items: [ .... ]
41062    }
41063 );
41064
41065 </code></pre>
41066      * @param {Object} cfg Xtype definition of item to add.
41067      */
41068     
41069     
41070     getChildContainer: function () {
41071         return this.getEl();
41072     },
41073     
41074     
41075     onScroll : function(e)
41076     {
41077         this.fireEvent('scroll', this, e);
41078     }
41079     
41080     
41081     /*
41082         var  ret = new Roo.factory(cfg);
41083         return ret;
41084         
41085         
41086         // add form..
41087         if (cfg.xtype.match(/^Form$/)) {
41088             
41089             var el;
41090             //if (this.footer) {
41091             //    el = this.footer.container.insertSibling(false, 'before');
41092             //} else {
41093                 el = this.el.createChild();
41094             //}
41095
41096             this.form = new  Roo.form.Form(cfg);
41097             
41098             
41099             if ( this.form.allItems.length) {
41100                 this.form.render(el.dom);
41101             }
41102             return this.form;
41103         }
41104         // should only have one of theses..
41105         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41106             // views.. should not be just added - used named prop 'view''
41107             
41108             cfg.el = this.el.appendChild(document.createElement("div"));
41109             // factory?
41110             
41111             var ret = new Roo.factory(cfg);
41112              
41113              ret.render && ret.render(false, ''); // render blank..
41114             this.view = ret;
41115             return ret;
41116         }
41117         return false;
41118     }
41119     \*/
41120 });
41121  
41122 /**
41123  * @class Roo.bootstrap.panel.Grid
41124  * @extends Roo.bootstrap.panel.Content
41125  * @constructor
41126  * Create a new GridPanel.
41127  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41128  * @param {Object} config A the config object
41129   
41130  */
41131
41132
41133
41134 Roo.bootstrap.panel.Grid = function(config)
41135 {
41136     
41137       
41138     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41139         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41140
41141     config.el = this.wrapper;
41142     //this.el = this.wrapper;
41143     
41144       if (config.container) {
41145         // ctor'ed from a Border/panel.grid
41146         
41147         
41148         this.wrapper.setStyle("overflow", "hidden");
41149         this.wrapper.addClass('roo-grid-container');
41150
41151     }
41152     
41153     
41154     if(config.toolbar){
41155         var tool_el = this.wrapper.createChild();    
41156         this.toolbar = Roo.factory(config.toolbar);
41157         var ti = [];
41158         if (config.toolbar.items) {
41159             ti = config.toolbar.items ;
41160             delete config.toolbar.items ;
41161         }
41162         
41163         var nitems = [];
41164         this.toolbar.render(tool_el);
41165         for(var i =0;i < ti.length;i++) {
41166           //  Roo.log(['add child', items[i]]);
41167             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41168         }
41169         this.toolbar.items = nitems;
41170         
41171         delete config.toolbar;
41172     }
41173     
41174     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41175     config.grid.scrollBody = true;;
41176     config.grid.monitorWindowResize = false; // turn off autosizing
41177     config.grid.autoHeight = false;
41178     config.grid.autoWidth = false;
41179     
41180     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41181     
41182     if (config.background) {
41183         // render grid on panel activation (if panel background)
41184         this.on('activate', function(gp) {
41185             if (!gp.grid.rendered) {
41186                 gp.grid.render(this.wrapper);
41187                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41188             }
41189         });
41190             
41191     } else {
41192         this.grid.render(this.wrapper);
41193         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41194
41195     }
41196     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41197     // ??? needed ??? config.el = this.wrapper;
41198     
41199     
41200     
41201   
41202     // xtype created footer. - not sure if will work as we normally have to render first..
41203     if (this.footer && !this.footer.el && this.footer.xtype) {
41204         
41205         var ctr = this.grid.getView().getFooterPanel(true);
41206         this.footer.dataSource = this.grid.dataSource;
41207         this.footer = Roo.factory(this.footer, Roo);
41208         this.footer.render(ctr);
41209         
41210     }
41211     
41212     
41213     
41214     
41215      
41216 };
41217
41218 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41219     getId : function(){
41220         return this.grid.id;
41221     },
41222     
41223     /**
41224      * Returns the grid for this panel
41225      * @return {Roo.bootstrap.Table} 
41226      */
41227     getGrid : function(){
41228         return this.grid;    
41229     },
41230     
41231     setSize : function(width, height){
41232         if(!this.ignoreResize(width, height)){
41233             var grid = this.grid;
41234             var size = this.adjustForComponents(width, height);
41235             // tfoot is not a footer?
41236           
41237             
41238             var gridel = grid.getGridEl();
41239             gridel.setSize(size.width, size.height);
41240             
41241             var tbd = grid.getGridEl().select('tbody', true).first();
41242             var thd = grid.getGridEl().select('thead',true).first();
41243             var tbf= grid.getGridEl().select('tfoot', true).first();
41244
41245             if (tbf) {
41246                 size.height -= tbf.getHeight();
41247             }
41248             if (thd) {
41249                 size.height -= thd.getHeight();
41250             }
41251             
41252             tbd.setSize(size.width, size.height );
41253             // this is for the account management tab -seems to work there.
41254             var thd = grid.getGridEl().select('thead',true).first();
41255             //if (tbd) {
41256             //    tbd.setSize(size.width, size.height - thd.getHeight());
41257             //}
41258              
41259             grid.autoSize();
41260         }
41261     },
41262      
41263     
41264     
41265     beforeSlide : function(){
41266         this.grid.getView().scroller.clip();
41267     },
41268     
41269     afterSlide : function(){
41270         this.grid.getView().scroller.unclip();
41271     },
41272     
41273     destroy : function(){
41274         this.grid.destroy();
41275         delete this.grid;
41276         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41277     }
41278 });
41279
41280 /**
41281  * @class Roo.bootstrap.panel.Nest
41282  * @extends Roo.bootstrap.panel.Content
41283  * @constructor
41284  * Create a new Panel, that can contain a layout.Border.
41285  * 
41286  * 
41287  * @param {Roo.BorderLayout} layout The layout for this panel
41288  * @param {String/Object} config A string to set only the title or a config object
41289  */
41290 Roo.bootstrap.panel.Nest = function(config)
41291 {
41292     // construct with only one argument..
41293     /* FIXME - implement nicer consturctors
41294     if (layout.layout) {
41295         config = layout;
41296         layout = config.layout;
41297         delete config.layout;
41298     }
41299     if (layout.xtype && !layout.getEl) {
41300         // then layout needs constructing..
41301         layout = Roo.factory(layout, Roo);
41302     }
41303     */
41304     
41305     config.el =  config.layout.getEl();
41306     
41307     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41308     
41309     config.layout.monitorWindowResize = false; // turn off autosizing
41310     this.layout = config.layout;
41311     this.layout.getEl().addClass("roo-layout-nested-layout");
41312     this.layout.parent = this;
41313     
41314     
41315     
41316     
41317 };
41318
41319 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41320
41321     setSize : function(width, height){
41322         if(!this.ignoreResize(width, height)){
41323             var size = this.adjustForComponents(width, height);
41324             var el = this.layout.getEl();
41325             if (size.height < 1) {
41326                 el.setWidth(size.width);   
41327             } else {
41328                 el.setSize(size.width, size.height);
41329             }
41330             var touch = el.dom.offsetWidth;
41331             this.layout.layout();
41332             // ie requires a double layout on the first pass
41333             if(Roo.isIE && !this.initialized){
41334                 this.initialized = true;
41335                 this.layout.layout();
41336             }
41337         }
41338     },
41339     
41340     // activate all subpanels if not currently active..
41341     
41342     setActiveState : function(active){
41343         this.active = active;
41344         this.setActiveClass(active);
41345         
41346         if(!active){
41347             this.fireEvent("deactivate", this);
41348             return;
41349         }
41350         
41351         this.fireEvent("activate", this);
41352         // not sure if this should happen before or after..
41353         if (!this.layout) {
41354             return; // should not happen..
41355         }
41356         var reg = false;
41357         for (var r in this.layout.regions) {
41358             reg = this.layout.getRegion(r);
41359             if (reg.getActivePanel()) {
41360                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41361                 reg.setActivePanel(reg.getActivePanel());
41362                 continue;
41363             }
41364             if (!reg.panels.length) {
41365                 continue;
41366             }
41367             reg.showPanel(reg.getPanel(0));
41368         }
41369         
41370         
41371         
41372         
41373     },
41374     
41375     /**
41376      * Returns the nested BorderLayout for this panel
41377      * @return {Roo.BorderLayout} 
41378      */
41379     getLayout : function(){
41380         return this.layout;
41381     },
41382     
41383      /**
41384      * Adds a xtype elements to the layout of the nested panel
41385      * <pre><code>
41386
41387 panel.addxtype({
41388        xtype : 'ContentPanel',
41389        region: 'west',
41390        items: [ .... ]
41391    }
41392 );
41393
41394 panel.addxtype({
41395         xtype : 'NestedLayoutPanel',
41396         region: 'west',
41397         layout: {
41398            center: { },
41399            west: { }   
41400         },
41401         items : [ ... list of content panels or nested layout panels.. ]
41402    }
41403 );
41404 </code></pre>
41405      * @param {Object} cfg Xtype definition of item to add.
41406      */
41407     addxtype : function(cfg) {
41408         return this.layout.addxtype(cfg);
41409     
41410     }
41411 });/*
41412  * Based on:
41413  * Ext JS Library 1.1.1
41414  * Copyright(c) 2006-2007, Ext JS, LLC.
41415  *
41416  * Originally Released Under LGPL - original licence link has changed is not relivant.
41417  *
41418  * Fork - LGPL
41419  * <script type="text/javascript">
41420  */
41421 /**
41422  * @class Roo.TabPanel
41423  * @extends Roo.util.Observable
41424  * A lightweight tab container.
41425  * <br><br>
41426  * Usage:
41427  * <pre><code>
41428 // basic tabs 1, built from existing content
41429 var tabs = new Roo.TabPanel("tabs1");
41430 tabs.addTab("script", "View Script");
41431 tabs.addTab("markup", "View Markup");
41432 tabs.activate("script");
41433
41434 // more advanced tabs, built from javascript
41435 var jtabs = new Roo.TabPanel("jtabs");
41436 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41437
41438 // set up the UpdateManager
41439 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41440 var updater = tab2.getUpdateManager();
41441 updater.setDefaultUrl("ajax1.htm");
41442 tab2.on('activate', updater.refresh, updater, true);
41443
41444 // Use setUrl for Ajax loading
41445 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41446 tab3.setUrl("ajax2.htm", null, true);
41447
41448 // Disabled tab
41449 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41450 tab4.disable();
41451
41452 jtabs.activate("jtabs-1");
41453  * </code></pre>
41454  * @constructor
41455  * Create a new TabPanel.
41456  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41457  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41458  */
41459 Roo.bootstrap.panel.Tabs = function(config){
41460     /**
41461     * The container element for this TabPanel.
41462     * @type Roo.Element
41463     */
41464     this.el = Roo.get(config.el);
41465     delete config.el;
41466     if(config){
41467         if(typeof config == "boolean"){
41468             this.tabPosition = config ? "bottom" : "top";
41469         }else{
41470             Roo.apply(this, config);
41471         }
41472     }
41473     
41474     if(this.tabPosition == "bottom"){
41475         // if tabs are at the bottom = create the body first.
41476         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41477         this.el.addClass("roo-tabs-bottom");
41478     }
41479     // next create the tabs holders
41480     
41481     if (this.tabPosition == "west"){
41482         
41483         var reg = this.region; // fake it..
41484         while (reg) {
41485             if (!reg.mgr.parent) {
41486                 break;
41487             }
41488             reg = reg.mgr.parent.region;
41489         }
41490         Roo.log("got nest?");
41491         Roo.log(reg);
41492         if (reg.mgr.getRegion('west')) {
41493             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41494             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41495             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41496             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41497             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41498         
41499             
41500         }
41501         
41502         
41503     } else {
41504      
41505         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41506         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41507         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41508         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41509     }
41510     
41511     
41512     if(Roo.isIE){
41513         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41514     }
41515     
41516     // finally - if tabs are at the top, then create the body last..
41517     if(this.tabPosition != "bottom"){
41518         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41519          * @type Roo.Element
41520          */
41521         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41522         this.el.addClass("roo-tabs-top");
41523     }
41524     this.items = [];
41525
41526     this.bodyEl.setStyle("position", "relative");
41527
41528     this.active = null;
41529     this.activateDelegate = this.activate.createDelegate(this);
41530
41531     this.addEvents({
41532         /**
41533          * @event tabchange
41534          * Fires when the active tab changes
41535          * @param {Roo.TabPanel} this
41536          * @param {Roo.TabPanelItem} activePanel The new active tab
41537          */
41538         "tabchange": true,
41539         /**
41540          * @event beforetabchange
41541          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41542          * @param {Roo.TabPanel} this
41543          * @param {Object} e Set cancel to true on this object to cancel the tab change
41544          * @param {Roo.TabPanelItem} tab The tab being changed to
41545          */
41546         "beforetabchange" : true
41547     });
41548
41549     Roo.EventManager.onWindowResize(this.onResize, this);
41550     this.cpad = this.el.getPadding("lr");
41551     this.hiddenCount = 0;
41552
41553
41554     // toolbar on the tabbar support...
41555     if (this.toolbar) {
41556         alert("no toolbar support yet");
41557         this.toolbar  = false;
41558         /*
41559         var tcfg = this.toolbar;
41560         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41561         this.toolbar = new Roo.Toolbar(tcfg);
41562         if (Roo.isSafari) {
41563             var tbl = tcfg.container.child('table', true);
41564             tbl.setAttribute('width', '100%');
41565         }
41566         */
41567         
41568     }
41569    
41570
41571
41572     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41573 };
41574
41575 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41576     /*
41577      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41578      */
41579     tabPosition : "top",
41580     /*
41581      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41582      */
41583     currentTabWidth : 0,
41584     /*
41585      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41586      */
41587     minTabWidth : 40,
41588     /*
41589      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41590      */
41591     maxTabWidth : 250,
41592     /*
41593      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41594      */
41595     preferredTabWidth : 175,
41596     /*
41597      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41598      */
41599     resizeTabs : false,
41600     /*
41601      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41602      */
41603     monitorResize : true,
41604     /*
41605      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41606      */
41607     toolbar : false,  // set by caller..
41608     
41609     region : false, /// set by caller
41610     
41611     disableTooltips : true, // not used yet...
41612
41613     /**
41614      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41615      * @param {String} id The id of the div to use <b>or create</b>
41616      * @param {String} text The text for the tab
41617      * @param {String} content (optional) Content to put in the TabPanelItem body
41618      * @param {Boolean} closable (optional) True to create a close icon on the tab
41619      * @return {Roo.TabPanelItem} The created TabPanelItem
41620      */
41621     addTab : function(id, text, content, closable, tpl)
41622     {
41623         var item = new Roo.bootstrap.panel.TabItem({
41624             panel: this,
41625             id : id,
41626             text : text,
41627             closable : closable,
41628             tpl : tpl
41629         });
41630         this.addTabItem(item);
41631         if(content){
41632             item.setContent(content);
41633         }
41634         return item;
41635     },
41636
41637     /**
41638      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41639      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41640      * @return {Roo.TabPanelItem}
41641      */
41642     getTab : function(id){
41643         return this.items[id];
41644     },
41645
41646     /**
41647      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41648      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41649      */
41650     hideTab : function(id){
41651         var t = this.items[id];
41652         if(!t.isHidden()){
41653            t.setHidden(true);
41654            this.hiddenCount++;
41655            this.autoSizeTabs();
41656         }
41657     },
41658
41659     /**
41660      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41661      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41662      */
41663     unhideTab : function(id){
41664         var t = this.items[id];
41665         if(t.isHidden()){
41666            t.setHidden(false);
41667            this.hiddenCount--;
41668            this.autoSizeTabs();
41669         }
41670     },
41671
41672     /**
41673      * Adds an existing {@link Roo.TabPanelItem}.
41674      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41675      */
41676     addTabItem : function(item)
41677     {
41678         this.items[item.id] = item;
41679         this.items.push(item);
41680         this.autoSizeTabs();
41681       //  if(this.resizeTabs){
41682     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41683   //         this.autoSizeTabs();
41684 //        }else{
41685 //            item.autoSize();
41686        // }
41687     },
41688
41689     /**
41690      * Removes a {@link Roo.TabPanelItem}.
41691      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41692      */
41693     removeTab : function(id){
41694         var items = this.items;
41695         var tab = items[id];
41696         if(!tab) { return; }
41697         var index = items.indexOf(tab);
41698         if(this.active == tab && items.length > 1){
41699             var newTab = this.getNextAvailable(index);
41700             if(newTab) {
41701                 newTab.activate();
41702             }
41703         }
41704         this.stripEl.dom.removeChild(tab.pnode.dom);
41705         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41706             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41707         }
41708         items.splice(index, 1);
41709         delete this.items[tab.id];
41710         tab.fireEvent("close", tab);
41711         tab.purgeListeners();
41712         this.autoSizeTabs();
41713     },
41714
41715     getNextAvailable : function(start){
41716         var items = this.items;
41717         var index = start;
41718         // look for a next tab that will slide over to
41719         // replace the one being removed
41720         while(index < items.length){
41721             var item = items[++index];
41722             if(item && !item.isHidden()){
41723                 return item;
41724             }
41725         }
41726         // if one isn't found select the previous tab (on the left)
41727         index = start;
41728         while(index >= 0){
41729             var item = items[--index];
41730             if(item && !item.isHidden()){
41731                 return item;
41732             }
41733         }
41734         return null;
41735     },
41736
41737     /**
41738      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41739      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41740      */
41741     disableTab : function(id){
41742         var tab = this.items[id];
41743         if(tab && this.active != tab){
41744             tab.disable();
41745         }
41746     },
41747
41748     /**
41749      * Enables a {@link Roo.TabPanelItem} that is disabled.
41750      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41751      */
41752     enableTab : function(id){
41753         var tab = this.items[id];
41754         tab.enable();
41755     },
41756
41757     /**
41758      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41759      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41760      * @return {Roo.TabPanelItem} The TabPanelItem.
41761      */
41762     activate : function(id)
41763     {
41764         //Roo.log('activite:'  + id);
41765         
41766         var tab = this.items[id];
41767         if(!tab){
41768             return null;
41769         }
41770         if(tab == this.active || tab.disabled){
41771             return tab;
41772         }
41773         var e = {};
41774         this.fireEvent("beforetabchange", this, e, tab);
41775         if(e.cancel !== true && !tab.disabled){
41776             if(this.active){
41777                 this.active.hide();
41778             }
41779             this.active = this.items[id];
41780             this.active.show();
41781             this.fireEvent("tabchange", this, this.active);
41782         }
41783         return tab;
41784     },
41785
41786     /**
41787      * Gets the active {@link Roo.TabPanelItem}.
41788      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41789      */
41790     getActiveTab : function(){
41791         return this.active;
41792     },
41793
41794     /**
41795      * Updates the tab body element to fit the height of the container element
41796      * for overflow scrolling
41797      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41798      */
41799     syncHeight : function(targetHeight){
41800         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41801         var bm = this.bodyEl.getMargins();
41802         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41803         this.bodyEl.setHeight(newHeight);
41804         return newHeight;
41805     },
41806
41807     onResize : function(){
41808         if(this.monitorResize){
41809             this.autoSizeTabs();
41810         }
41811     },
41812
41813     /**
41814      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41815      */
41816     beginUpdate : function(){
41817         this.updating = true;
41818     },
41819
41820     /**
41821      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41822      */
41823     endUpdate : function(){
41824         this.updating = false;
41825         this.autoSizeTabs();
41826     },
41827
41828     /**
41829      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41830      */
41831     autoSizeTabs : function()
41832     {
41833         var count = this.items.length;
41834         var vcount = count - this.hiddenCount;
41835         
41836         if (vcount < 2) {
41837             this.stripEl.hide();
41838         } else {
41839             this.stripEl.show();
41840         }
41841         
41842         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41843             return;
41844         }
41845         
41846         
41847         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41848         var availWidth = Math.floor(w / vcount);
41849         var b = this.stripBody;
41850         if(b.getWidth() > w){
41851             var tabs = this.items;
41852             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41853             if(availWidth < this.minTabWidth){
41854                 /*if(!this.sleft){    // incomplete scrolling code
41855                     this.createScrollButtons();
41856                 }
41857                 this.showScroll();
41858                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41859             }
41860         }else{
41861             if(this.currentTabWidth < this.preferredTabWidth){
41862                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41863             }
41864         }
41865     },
41866
41867     /**
41868      * Returns the number of tabs in this TabPanel.
41869      * @return {Number}
41870      */
41871      getCount : function(){
41872          return this.items.length;
41873      },
41874
41875     /**
41876      * Resizes all the tabs to the passed width
41877      * @param {Number} The new width
41878      */
41879     setTabWidth : function(width){
41880         this.currentTabWidth = width;
41881         for(var i = 0, len = this.items.length; i < len; i++) {
41882                 if(!this.items[i].isHidden()) {
41883                 this.items[i].setWidth(width);
41884             }
41885         }
41886     },
41887
41888     /**
41889      * Destroys this TabPanel
41890      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41891      */
41892     destroy : function(removeEl){
41893         Roo.EventManager.removeResizeListener(this.onResize, this);
41894         for(var i = 0, len = this.items.length; i < len; i++){
41895             this.items[i].purgeListeners();
41896         }
41897         if(removeEl === true){
41898             this.el.update("");
41899             this.el.remove();
41900         }
41901     },
41902     
41903     createStrip : function(container)
41904     {
41905         var strip = document.createElement("nav");
41906         strip.className = Roo.bootstrap.version == 4 ?
41907             "navbar-light bg-light" : 
41908             "navbar navbar-default"; //"x-tabs-wrap";
41909         container.appendChild(strip);
41910         return strip;
41911     },
41912     
41913     createStripList : function(strip)
41914     {
41915         // div wrapper for retard IE
41916         // returns the "tr" element.
41917         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41918         //'<div class="x-tabs-strip-wrap">'+
41919           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41920           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41921         return strip.firstChild; //.firstChild.firstChild.firstChild;
41922     },
41923     createBody : function(container)
41924     {
41925         var body = document.createElement("div");
41926         Roo.id(body, "tab-body");
41927         //Roo.fly(body).addClass("x-tabs-body");
41928         Roo.fly(body).addClass("tab-content");
41929         container.appendChild(body);
41930         return body;
41931     },
41932     createItemBody :function(bodyEl, id){
41933         var body = Roo.getDom(id);
41934         if(!body){
41935             body = document.createElement("div");
41936             body.id = id;
41937         }
41938         //Roo.fly(body).addClass("x-tabs-item-body");
41939         Roo.fly(body).addClass("tab-pane");
41940          bodyEl.insertBefore(body, bodyEl.firstChild);
41941         return body;
41942     },
41943     /** @private */
41944     createStripElements :  function(stripEl, text, closable, tpl)
41945     {
41946         var td = document.createElement("li"); // was td..
41947         td.className = 'nav-item';
41948         
41949         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41950         
41951         
41952         stripEl.appendChild(td);
41953         /*if(closable){
41954             td.className = "x-tabs-closable";
41955             if(!this.closeTpl){
41956                 this.closeTpl = new Roo.Template(
41957                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41958                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41959                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41960                 );
41961             }
41962             var el = this.closeTpl.overwrite(td, {"text": text});
41963             var close = el.getElementsByTagName("div")[0];
41964             var inner = el.getElementsByTagName("em")[0];
41965             return {"el": el, "close": close, "inner": inner};
41966         } else {
41967         */
41968         // not sure what this is..
41969 //            if(!this.tabTpl){
41970                 //this.tabTpl = new Roo.Template(
41971                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41972                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41973                 //);
41974 //                this.tabTpl = new Roo.Template(
41975 //                   '<a href="#">' +
41976 //                   '<span unselectable="on"' +
41977 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41978 //                            ' >{text}</span></a>'
41979 //                );
41980 //                
41981 //            }
41982
41983
41984             var template = tpl || this.tabTpl || false;
41985             
41986             if(!template){
41987                 template =  new Roo.Template(
41988                         Roo.bootstrap.version == 4 ? 
41989                             (
41990                                 '<a class="nav-link" href="#" unselectable="on"' +
41991                                      (this.disableTooltips ? '' : ' title="{text}"') +
41992                                      ' >{text}</a>'
41993                             ) : (
41994                                 '<a class="nav-link" href="#">' +
41995                                 '<span unselectable="on"' +
41996                                          (this.disableTooltips ? '' : ' title="{text}"') +
41997                                     ' >{text}</span></a>'
41998                             )
41999                 );
42000             }
42001             
42002             switch (typeof(template)) {
42003                 case 'object' :
42004                     break;
42005                 case 'string' :
42006                     template = new Roo.Template(template);
42007                     break;
42008                 default :
42009                     break;
42010             }
42011             
42012             var el = template.overwrite(td, {"text": text});
42013             
42014             var inner = el.getElementsByTagName("span")[0];
42015             
42016             return {"el": el, "inner": inner};
42017             
42018     }
42019         
42020     
42021 });
42022
42023 /**
42024  * @class Roo.TabPanelItem
42025  * @extends Roo.util.Observable
42026  * Represents an individual item (tab plus body) in a TabPanel.
42027  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42028  * @param {String} id The id of this TabPanelItem
42029  * @param {String} text The text for the tab of this TabPanelItem
42030  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42031  */
42032 Roo.bootstrap.panel.TabItem = function(config){
42033     /**
42034      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42035      * @type Roo.TabPanel
42036      */
42037     this.tabPanel = config.panel;
42038     /**
42039      * The id for this TabPanelItem
42040      * @type String
42041      */
42042     this.id = config.id;
42043     /** @private */
42044     this.disabled = false;
42045     /** @private */
42046     this.text = config.text;
42047     /** @private */
42048     this.loaded = false;
42049     this.closable = config.closable;
42050
42051     /**
42052      * The body element for this TabPanelItem.
42053      * @type Roo.Element
42054      */
42055     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42056     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42057     this.bodyEl.setStyle("display", "block");
42058     this.bodyEl.setStyle("zoom", "1");
42059     //this.hideAction();
42060
42061     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42062     /** @private */
42063     this.el = Roo.get(els.el);
42064     this.inner = Roo.get(els.inner, true);
42065      this.textEl = Roo.bootstrap.version == 4 ?
42066         this.el : Roo.get(this.el.dom.firstChild, true);
42067
42068     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42069     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42070
42071     
42072 //    this.el.on("mousedown", this.onTabMouseDown, this);
42073     this.el.on("click", this.onTabClick, this);
42074     /** @private */
42075     if(config.closable){
42076         var c = Roo.get(els.close, true);
42077         c.dom.title = this.closeText;
42078         c.addClassOnOver("close-over");
42079         c.on("click", this.closeClick, this);
42080      }
42081
42082     this.addEvents({
42083          /**
42084          * @event activate
42085          * Fires when this tab becomes the active tab.
42086          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42087          * @param {Roo.TabPanelItem} this
42088          */
42089         "activate": true,
42090         /**
42091          * @event beforeclose
42092          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42093          * @param {Roo.TabPanelItem} this
42094          * @param {Object} e Set cancel to true on this object to cancel the close.
42095          */
42096         "beforeclose": true,
42097         /**
42098          * @event close
42099          * Fires when this tab is closed.
42100          * @param {Roo.TabPanelItem} this
42101          */
42102          "close": true,
42103         /**
42104          * @event deactivate
42105          * Fires when this tab is no longer the active tab.
42106          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42107          * @param {Roo.TabPanelItem} this
42108          */
42109          "deactivate" : true
42110     });
42111     this.hidden = false;
42112
42113     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42114 };
42115
42116 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42117            {
42118     purgeListeners : function(){
42119        Roo.util.Observable.prototype.purgeListeners.call(this);
42120        this.el.removeAllListeners();
42121     },
42122     /**
42123      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42124      */
42125     show : function(){
42126         this.status_node.addClass("active");
42127         this.showAction();
42128         if(Roo.isOpera){
42129             this.tabPanel.stripWrap.repaint();
42130         }
42131         this.fireEvent("activate", this.tabPanel, this);
42132     },
42133
42134     /**
42135      * Returns true if this tab is the active tab.
42136      * @return {Boolean}
42137      */
42138     isActive : function(){
42139         return this.tabPanel.getActiveTab() == this;
42140     },
42141
42142     /**
42143      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42144      */
42145     hide : function(){
42146         this.status_node.removeClass("active");
42147         this.hideAction();
42148         this.fireEvent("deactivate", this.tabPanel, this);
42149     },
42150
42151     hideAction : function(){
42152         this.bodyEl.hide();
42153         this.bodyEl.setStyle("position", "absolute");
42154         this.bodyEl.setLeft("-20000px");
42155         this.bodyEl.setTop("-20000px");
42156     },
42157
42158     showAction : function(){
42159         this.bodyEl.setStyle("position", "relative");
42160         this.bodyEl.setTop("");
42161         this.bodyEl.setLeft("");
42162         this.bodyEl.show();
42163     },
42164
42165     /**
42166      * Set the tooltip for the tab.
42167      * @param {String} tooltip The tab's tooltip
42168      */
42169     setTooltip : function(text){
42170         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42171             this.textEl.dom.qtip = text;
42172             this.textEl.dom.removeAttribute('title');
42173         }else{
42174             this.textEl.dom.title = text;
42175         }
42176     },
42177
42178     onTabClick : function(e){
42179         e.preventDefault();
42180         this.tabPanel.activate(this.id);
42181     },
42182
42183     onTabMouseDown : function(e){
42184         e.preventDefault();
42185         this.tabPanel.activate(this.id);
42186     },
42187 /*
42188     getWidth : function(){
42189         return this.inner.getWidth();
42190     },
42191
42192     setWidth : function(width){
42193         var iwidth = width - this.linode.getPadding("lr");
42194         this.inner.setWidth(iwidth);
42195         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42196         this.linode.setWidth(width);
42197     },
42198 */
42199     /**
42200      * Show or hide the tab
42201      * @param {Boolean} hidden True to hide or false to show.
42202      */
42203     setHidden : function(hidden){
42204         this.hidden = hidden;
42205         this.linode.setStyle("display", hidden ? "none" : "");
42206     },
42207
42208     /**
42209      * Returns true if this tab is "hidden"
42210      * @return {Boolean}
42211      */
42212     isHidden : function(){
42213         return this.hidden;
42214     },
42215
42216     /**
42217      * Returns the text for this tab
42218      * @return {String}
42219      */
42220     getText : function(){
42221         return this.text;
42222     },
42223     /*
42224     autoSize : function(){
42225         //this.el.beginMeasure();
42226         this.textEl.setWidth(1);
42227         /*
42228          *  #2804 [new] Tabs in Roojs
42229          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42230          */
42231         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42232         //this.el.endMeasure();
42233     //},
42234
42235     /**
42236      * Sets the text for the tab (Note: this also sets the tooltip text)
42237      * @param {String} text The tab's text and tooltip
42238      */
42239     setText : function(text){
42240         this.text = text;
42241         this.textEl.update(text);
42242         this.setTooltip(text);
42243         //if(!this.tabPanel.resizeTabs){
42244         //    this.autoSize();
42245         //}
42246     },
42247     /**
42248      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42249      */
42250     activate : function(){
42251         this.tabPanel.activate(this.id);
42252     },
42253
42254     /**
42255      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42256      */
42257     disable : function(){
42258         if(this.tabPanel.active != this){
42259             this.disabled = true;
42260             this.status_node.addClass("disabled");
42261         }
42262     },
42263
42264     /**
42265      * Enables this TabPanelItem if it was previously disabled.
42266      */
42267     enable : function(){
42268         this.disabled = false;
42269         this.status_node.removeClass("disabled");
42270     },
42271
42272     /**
42273      * Sets the content for this TabPanelItem.
42274      * @param {String} content The content
42275      * @param {Boolean} loadScripts true to look for and load scripts
42276      */
42277     setContent : function(content, loadScripts){
42278         this.bodyEl.update(content, loadScripts);
42279     },
42280
42281     /**
42282      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42283      * @return {Roo.UpdateManager} The UpdateManager
42284      */
42285     getUpdateManager : function(){
42286         return this.bodyEl.getUpdateManager();
42287     },
42288
42289     /**
42290      * Set a URL to be used to load the content for this TabPanelItem.
42291      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42292      * @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)
42293      * @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)
42294      * @return {Roo.UpdateManager} The UpdateManager
42295      */
42296     setUrl : function(url, params, loadOnce){
42297         if(this.refreshDelegate){
42298             this.un('activate', this.refreshDelegate);
42299         }
42300         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42301         this.on("activate", this.refreshDelegate);
42302         return this.bodyEl.getUpdateManager();
42303     },
42304
42305     /** @private */
42306     _handleRefresh : function(url, params, loadOnce){
42307         if(!loadOnce || !this.loaded){
42308             var updater = this.bodyEl.getUpdateManager();
42309             updater.update(url, params, this._setLoaded.createDelegate(this));
42310         }
42311     },
42312
42313     /**
42314      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42315      *   Will fail silently if the setUrl method has not been called.
42316      *   This does not activate the panel, just updates its content.
42317      */
42318     refresh : function(){
42319         if(this.refreshDelegate){
42320            this.loaded = false;
42321            this.refreshDelegate();
42322         }
42323     },
42324
42325     /** @private */
42326     _setLoaded : function(){
42327         this.loaded = true;
42328     },
42329
42330     /** @private */
42331     closeClick : function(e){
42332         var o = {};
42333         e.stopEvent();
42334         this.fireEvent("beforeclose", this, o);
42335         if(o.cancel !== true){
42336             this.tabPanel.removeTab(this.id);
42337         }
42338     },
42339     /**
42340      * The text displayed in the tooltip for the close icon.
42341      * @type String
42342      */
42343     closeText : "Close this tab"
42344 });
42345 /**
42346 *    This script refer to:
42347 *    Title: International Telephone Input
42348 *    Author: Jack O'Connor
42349 *    Code version:  v12.1.12
42350 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42351 **/
42352
42353 Roo.bootstrap.PhoneInputData = function() {
42354     var d = [
42355       [
42356         "Afghanistan (‫افغانستان‬‎)",
42357         "af",
42358         "93"
42359       ],
42360       [
42361         "Albania (Shqipëri)",
42362         "al",
42363         "355"
42364       ],
42365       [
42366         "Algeria (‫الجزائر‬‎)",
42367         "dz",
42368         "213"
42369       ],
42370       [
42371         "American Samoa",
42372         "as",
42373         "1684"
42374       ],
42375       [
42376         "Andorra",
42377         "ad",
42378         "376"
42379       ],
42380       [
42381         "Angola",
42382         "ao",
42383         "244"
42384       ],
42385       [
42386         "Anguilla",
42387         "ai",
42388         "1264"
42389       ],
42390       [
42391         "Antigua and Barbuda",
42392         "ag",
42393         "1268"
42394       ],
42395       [
42396         "Argentina",
42397         "ar",
42398         "54"
42399       ],
42400       [
42401         "Armenia (Հայաստան)",
42402         "am",
42403         "374"
42404       ],
42405       [
42406         "Aruba",
42407         "aw",
42408         "297"
42409       ],
42410       [
42411         "Australia",
42412         "au",
42413         "61",
42414         0
42415       ],
42416       [
42417         "Austria (Österreich)",
42418         "at",
42419         "43"
42420       ],
42421       [
42422         "Azerbaijan (Azərbaycan)",
42423         "az",
42424         "994"
42425       ],
42426       [
42427         "Bahamas",
42428         "bs",
42429         "1242"
42430       ],
42431       [
42432         "Bahrain (‫البحرين‬‎)",
42433         "bh",
42434         "973"
42435       ],
42436       [
42437         "Bangladesh (বাংলাদেশ)",
42438         "bd",
42439         "880"
42440       ],
42441       [
42442         "Barbados",
42443         "bb",
42444         "1246"
42445       ],
42446       [
42447         "Belarus (Беларусь)",
42448         "by",
42449         "375"
42450       ],
42451       [
42452         "Belgium (België)",
42453         "be",
42454         "32"
42455       ],
42456       [
42457         "Belize",
42458         "bz",
42459         "501"
42460       ],
42461       [
42462         "Benin (Bénin)",
42463         "bj",
42464         "229"
42465       ],
42466       [
42467         "Bermuda",
42468         "bm",
42469         "1441"
42470       ],
42471       [
42472         "Bhutan (འབྲུག)",
42473         "bt",
42474         "975"
42475       ],
42476       [
42477         "Bolivia",
42478         "bo",
42479         "591"
42480       ],
42481       [
42482         "Bosnia and Herzegovina (Босна и Херцеговина)",
42483         "ba",
42484         "387"
42485       ],
42486       [
42487         "Botswana",
42488         "bw",
42489         "267"
42490       ],
42491       [
42492         "Brazil (Brasil)",
42493         "br",
42494         "55"
42495       ],
42496       [
42497         "British Indian Ocean Territory",
42498         "io",
42499         "246"
42500       ],
42501       [
42502         "British Virgin Islands",
42503         "vg",
42504         "1284"
42505       ],
42506       [
42507         "Brunei",
42508         "bn",
42509         "673"
42510       ],
42511       [
42512         "Bulgaria (България)",
42513         "bg",
42514         "359"
42515       ],
42516       [
42517         "Burkina Faso",
42518         "bf",
42519         "226"
42520       ],
42521       [
42522         "Burundi (Uburundi)",
42523         "bi",
42524         "257"
42525       ],
42526       [
42527         "Cambodia (កម្ពុជា)",
42528         "kh",
42529         "855"
42530       ],
42531       [
42532         "Cameroon (Cameroun)",
42533         "cm",
42534         "237"
42535       ],
42536       [
42537         "Canada",
42538         "ca",
42539         "1",
42540         1,
42541         ["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"]
42542       ],
42543       [
42544         "Cape Verde (Kabu Verdi)",
42545         "cv",
42546         "238"
42547       ],
42548       [
42549         "Caribbean Netherlands",
42550         "bq",
42551         "599",
42552         1
42553       ],
42554       [
42555         "Cayman Islands",
42556         "ky",
42557         "1345"
42558       ],
42559       [
42560         "Central African Republic (République centrafricaine)",
42561         "cf",
42562         "236"
42563       ],
42564       [
42565         "Chad (Tchad)",
42566         "td",
42567         "235"
42568       ],
42569       [
42570         "Chile",
42571         "cl",
42572         "56"
42573       ],
42574       [
42575         "China (中国)",
42576         "cn",
42577         "86"
42578       ],
42579       [
42580         "Christmas Island",
42581         "cx",
42582         "61",
42583         2
42584       ],
42585       [
42586         "Cocos (Keeling) Islands",
42587         "cc",
42588         "61",
42589         1
42590       ],
42591       [
42592         "Colombia",
42593         "co",
42594         "57"
42595       ],
42596       [
42597         "Comoros (‫جزر القمر‬‎)",
42598         "km",
42599         "269"
42600       ],
42601       [
42602         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42603         "cd",
42604         "243"
42605       ],
42606       [
42607         "Congo (Republic) (Congo-Brazzaville)",
42608         "cg",
42609         "242"
42610       ],
42611       [
42612         "Cook Islands",
42613         "ck",
42614         "682"
42615       ],
42616       [
42617         "Costa Rica",
42618         "cr",
42619         "506"
42620       ],
42621       [
42622         "Côte d’Ivoire",
42623         "ci",
42624         "225"
42625       ],
42626       [
42627         "Croatia (Hrvatska)",
42628         "hr",
42629         "385"
42630       ],
42631       [
42632         "Cuba",
42633         "cu",
42634         "53"
42635       ],
42636       [
42637         "Curaçao",
42638         "cw",
42639         "599",
42640         0
42641       ],
42642       [
42643         "Cyprus (Κύπρος)",
42644         "cy",
42645         "357"
42646       ],
42647       [
42648         "Czech Republic (Česká republika)",
42649         "cz",
42650         "420"
42651       ],
42652       [
42653         "Denmark (Danmark)",
42654         "dk",
42655         "45"
42656       ],
42657       [
42658         "Djibouti",
42659         "dj",
42660         "253"
42661       ],
42662       [
42663         "Dominica",
42664         "dm",
42665         "1767"
42666       ],
42667       [
42668         "Dominican Republic (República Dominicana)",
42669         "do",
42670         "1",
42671         2,
42672         ["809", "829", "849"]
42673       ],
42674       [
42675         "Ecuador",
42676         "ec",
42677         "593"
42678       ],
42679       [
42680         "Egypt (‫مصر‬‎)",
42681         "eg",
42682         "20"
42683       ],
42684       [
42685         "El Salvador",
42686         "sv",
42687         "503"
42688       ],
42689       [
42690         "Equatorial Guinea (Guinea Ecuatorial)",
42691         "gq",
42692         "240"
42693       ],
42694       [
42695         "Eritrea",
42696         "er",
42697         "291"
42698       ],
42699       [
42700         "Estonia (Eesti)",
42701         "ee",
42702         "372"
42703       ],
42704       [
42705         "Ethiopia",
42706         "et",
42707         "251"
42708       ],
42709       [
42710         "Falkland Islands (Islas Malvinas)",
42711         "fk",
42712         "500"
42713       ],
42714       [
42715         "Faroe Islands (Føroyar)",
42716         "fo",
42717         "298"
42718       ],
42719       [
42720         "Fiji",
42721         "fj",
42722         "679"
42723       ],
42724       [
42725         "Finland (Suomi)",
42726         "fi",
42727         "358",
42728         0
42729       ],
42730       [
42731         "France",
42732         "fr",
42733         "33"
42734       ],
42735       [
42736         "French Guiana (Guyane française)",
42737         "gf",
42738         "594"
42739       ],
42740       [
42741         "French Polynesia (Polynésie française)",
42742         "pf",
42743         "689"
42744       ],
42745       [
42746         "Gabon",
42747         "ga",
42748         "241"
42749       ],
42750       [
42751         "Gambia",
42752         "gm",
42753         "220"
42754       ],
42755       [
42756         "Georgia (საქართველო)",
42757         "ge",
42758         "995"
42759       ],
42760       [
42761         "Germany (Deutschland)",
42762         "de",
42763         "49"
42764       ],
42765       [
42766         "Ghana (Gaana)",
42767         "gh",
42768         "233"
42769       ],
42770       [
42771         "Gibraltar",
42772         "gi",
42773         "350"
42774       ],
42775       [
42776         "Greece (Ελλάδα)",
42777         "gr",
42778         "30"
42779       ],
42780       [
42781         "Greenland (Kalaallit Nunaat)",
42782         "gl",
42783         "299"
42784       ],
42785       [
42786         "Grenada",
42787         "gd",
42788         "1473"
42789       ],
42790       [
42791         "Guadeloupe",
42792         "gp",
42793         "590",
42794         0
42795       ],
42796       [
42797         "Guam",
42798         "gu",
42799         "1671"
42800       ],
42801       [
42802         "Guatemala",
42803         "gt",
42804         "502"
42805       ],
42806       [
42807         "Guernsey",
42808         "gg",
42809         "44",
42810         1
42811       ],
42812       [
42813         "Guinea (Guinée)",
42814         "gn",
42815         "224"
42816       ],
42817       [
42818         "Guinea-Bissau (Guiné Bissau)",
42819         "gw",
42820         "245"
42821       ],
42822       [
42823         "Guyana",
42824         "gy",
42825         "592"
42826       ],
42827       [
42828         "Haiti",
42829         "ht",
42830         "509"
42831       ],
42832       [
42833         "Honduras",
42834         "hn",
42835         "504"
42836       ],
42837       [
42838         "Hong Kong (香港)",
42839         "hk",
42840         "852"
42841       ],
42842       [
42843         "Hungary (Magyarország)",
42844         "hu",
42845         "36"
42846       ],
42847       [
42848         "Iceland (Ísland)",
42849         "is",
42850         "354"
42851       ],
42852       [
42853         "India (भारत)",
42854         "in",
42855         "91"
42856       ],
42857       [
42858         "Indonesia",
42859         "id",
42860         "62"
42861       ],
42862       [
42863         "Iran (‫ایران‬‎)",
42864         "ir",
42865         "98"
42866       ],
42867       [
42868         "Iraq (‫العراق‬‎)",
42869         "iq",
42870         "964"
42871       ],
42872       [
42873         "Ireland",
42874         "ie",
42875         "353"
42876       ],
42877       [
42878         "Isle of Man",
42879         "im",
42880         "44",
42881         2
42882       ],
42883       [
42884         "Israel (‫ישראל‬‎)",
42885         "il",
42886         "972"
42887       ],
42888       [
42889         "Italy (Italia)",
42890         "it",
42891         "39",
42892         0
42893       ],
42894       [
42895         "Jamaica",
42896         "jm",
42897         "1876"
42898       ],
42899       [
42900         "Japan (日本)",
42901         "jp",
42902         "81"
42903       ],
42904       [
42905         "Jersey",
42906         "je",
42907         "44",
42908         3
42909       ],
42910       [
42911         "Jordan (‫الأردن‬‎)",
42912         "jo",
42913         "962"
42914       ],
42915       [
42916         "Kazakhstan (Казахстан)",
42917         "kz",
42918         "7",
42919         1
42920       ],
42921       [
42922         "Kenya",
42923         "ke",
42924         "254"
42925       ],
42926       [
42927         "Kiribati",
42928         "ki",
42929         "686"
42930       ],
42931       [
42932         "Kosovo",
42933         "xk",
42934         "383"
42935       ],
42936       [
42937         "Kuwait (‫الكويت‬‎)",
42938         "kw",
42939         "965"
42940       ],
42941       [
42942         "Kyrgyzstan (Кыргызстан)",
42943         "kg",
42944         "996"
42945       ],
42946       [
42947         "Laos (ລາວ)",
42948         "la",
42949         "856"
42950       ],
42951       [
42952         "Latvia (Latvija)",
42953         "lv",
42954         "371"
42955       ],
42956       [
42957         "Lebanon (‫لبنان‬‎)",
42958         "lb",
42959         "961"
42960       ],
42961       [
42962         "Lesotho",
42963         "ls",
42964         "266"
42965       ],
42966       [
42967         "Liberia",
42968         "lr",
42969         "231"
42970       ],
42971       [
42972         "Libya (‫ليبيا‬‎)",
42973         "ly",
42974         "218"
42975       ],
42976       [
42977         "Liechtenstein",
42978         "li",
42979         "423"
42980       ],
42981       [
42982         "Lithuania (Lietuva)",
42983         "lt",
42984         "370"
42985       ],
42986       [
42987         "Luxembourg",
42988         "lu",
42989         "352"
42990       ],
42991       [
42992         "Macau (澳門)",
42993         "mo",
42994         "853"
42995       ],
42996       [
42997         "Macedonia (FYROM) (Македонија)",
42998         "mk",
42999         "389"
43000       ],
43001       [
43002         "Madagascar (Madagasikara)",
43003         "mg",
43004         "261"
43005       ],
43006       [
43007         "Malawi",
43008         "mw",
43009         "265"
43010       ],
43011       [
43012         "Malaysia",
43013         "my",
43014         "60"
43015       ],
43016       [
43017         "Maldives",
43018         "mv",
43019         "960"
43020       ],
43021       [
43022         "Mali",
43023         "ml",
43024         "223"
43025       ],
43026       [
43027         "Malta",
43028         "mt",
43029         "356"
43030       ],
43031       [
43032         "Marshall Islands",
43033         "mh",
43034         "692"
43035       ],
43036       [
43037         "Martinique",
43038         "mq",
43039         "596"
43040       ],
43041       [
43042         "Mauritania (‫موريتانيا‬‎)",
43043         "mr",
43044         "222"
43045       ],
43046       [
43047         "Mauritius (Moris)",
43048         "mu",
43049         "230"
43050       ],
43051       [
43052         "Mayotte",
43053         "yt",
43054         "262",
43055         1
43056       ],
43057       [
43058         "Mexico (México)",
43059         "mx",
43060         "52"
43061       ],
43062       [
43063         "Micronesia",
43064         "fm",
43065         "691"
43066       ],
43067       [
43068         "Moldova (Republica Moldova)",
43069         "md",
43070         "373"
43071       ],
43072       [
43073         "Monaco",
43074         "mc",
43075         "377"
43076       ],
43077       [
43078         "Mongolia (Монгол)",
43079         "mn",
43080         "976"
43081       ],
43082       [
43083         "Montenegro (Crna Gora)",
43084         "me",
43085         "382"
43086       ],
43087       [
43088         "Montserrat",
43089         "ms",
43090         "1664"
43091       ],
43092       [
43093         "Morocco (‫المغرب‬‎)",
43094         "ma",
43095         "212",
43096         0
43097       ],
43098       [
43099         "Mozambique (Moçambique)",
43100         "mz",
43101         "258"
43102       ],
43103       [
43104         "Myanmar (Burma) (မြန်မာ)",
43105         "mm",
43106         "95"
43107       ],
43108       [
43109         "Namibia (Namibië)",
43110         "na",
43111         "264"
43112       ],
43113       [
43114         "Nauru",
43115         "nr",
43116         "674"
43117       ],
43118       [
43119         "Nepal (नेपाल)",
43120         "np",
43121         "977"
43122       ],
43123       [
43124         "Netherlands (Nederland)",
43125         "nl",
43126         "31"
43127       ],
43128       [
43129         "New Caledonia (Nouvelle-Calédonie)",
43130         "nc",
43131         "687"
43132       ],
43133       [
43134         "New Zealand",
43135         "nz",
43136         "64"
43137       ],
43138       [
43139         "Nicaragua",
43140         "ni",
43141         "505"
43142       ],
43143       [
43144         "Niger (Nijar)",
43145         "ne",
43146         "227"
43147       ],
43148       [
43149         "Nigeria",
43150         "ng",
43151         "234"
43152       ],
43153       [
43154         "Niue",
43155         "nu",
43156         "683"
43157       ],
43158       [
43159         "Norfolk Island",
43160         "nf",
43161         "672"
43162       ],
43163       [
43164         "North Korea (조선 민주주의 인민 공화국)",
43165         "kp",
43166         "850"
43167       ],
43168       [
43169         "Northern Mariana Islands",
43170         "mp",
43171         "1670"
43172       ],
43173       [
43174         "Norway (Norge)",
43175         "no",
43176         "47",
43177         0
43178       ],
43179       [
43180         "Oman (‫عُمان‬‎)",
43181         "om",
43182         "968"
43183       ],
43184       [
43185         "Pakistan (‫پاکستان‬‎)",
43186         "pk",
43187         "92"
43188       ],
43189       [
43190         "Palau",
43191         "pw",
43192         "680"
43193       ],
43194       [
43195         "Palestine (‫فلسطين‬‎)",
43196         "ps",
43197         "970"
43198       ],
43199       [
43200         "Panama (Panamá)",
43201         "pa",
43202         "507"
43203       ],
43204       [
43205         "Papua New Guinea",
43206         "pg",
43207         "675"
43208       ],
43209       [
43210         "Paraguay",
43211         "py",
43212         "595"
43213       ],
43214       [
43215         "Peru (Perú)",
43216         "pe",
43217         "51"
43218       ],
43219       [
43220         "Philippines",
43221         "ph",
43222         "63"
43223       ],
43224       [
43225         "Poland (Polska)",
43226         "pl",
43227         "48"
43228       ],
43229       [
43230         "Portugal",
43231         "pt",
43232         "351"
43233       ],
43234       [
43235         "Puerto Rico",
43236         "pr",
43237         "1",
43238         3,
43239         ["787", "939"]
43240       ],
43241       [
43242         "Qatar (‫قطر‬‎)",
43243         "qa",
43244         "974"
43245       ],
43246       [
43247         "Réunion (La Réunion)",
43248         "re",
43249         "262",
43250         0
43251       ],
43252       [
43253         "Romania (România)",
43254         "ro",
43255         "40"
43256       ],
43257       [
43258         "Russia (Россия)",
43259         "ru",
43260         "7",
43261         0
43262       ],
43263       [
43264         "Rwanda",
43265         "rw",
43266         "250"
43267       ],
43268       [
43269         "Saint Barthélemy",
43270         "bl",
43271         "590",
43272         1
43273       ],
43274       [
43275         "Saint Helena",
43276         "sh",
43277         "290"
43278       ],
43279       [
43280         "Saint Kitts and Nevis",
43281         "kn",
43282         "1869"
43283       ],
43284       [
43285         "Saint Lucia",
43286         "lc",
43287         "1758"
43288       ],
43289       [
43290         "Saint Martin (Saint-Martin (partie française))",
43291         "mf",
43292         "590",
43293         2
43294       ],
43295       [
43296         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43297         "pm",
43298         "508"
43299       ],
43300       [
43301         "Saint Vincent and the Grenadines",
43302         "vc",
43303         "1784"
43304       ],
43305       [
43306         "Samoa",
43307         "ws",
43308         "685"
43309       ],
43310       [
43311         "San Marino",
43312         "sm",
43313         "378"
43314       ],
43315       [
43316         "São Tomé and Príncipe (São Tomé e Príncipe)",
43317         "st",
43318         "239"
43319       ],
43320       [
43321         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43322         "sa",
43323         "966"
43324       ],
43325       [
43326         "Senegal (Sénégal)",
43327         "sn",
43328         "221"
43329       ],
43330       [
43331         "Serbia (Србија)",
43332         "rs",
43333         "381"
43334       ],
43335       [
43336         "Seychelles",
43337         "sc",
43338         "248"
43339       ],
43340       [
43341         "Sierra Leone",
43342         "sl",
43343         "232"
43344       ],
43345       [
43346         "Singapore",
43347         "sg",
43348         "65"
43349       ],
43350       [
43351         "Sint Maarten",
43352         "sx",
43353         "1721"
43354       ],
43355       [
43356         "Slovakia (Slovensko)",
43357         "sk",
43358         "421"
43359       ],
43360       [
43361         "Slovenia (Slovenija)",
43362         "si",
43363         "386"
43364       ],
43365       [
43366         "Solomon Islands",
43367         "sb",
43368         "677"
43369       ],
43370       [
43371         "Somalia (Soomaaliya)",
43372         "so",
43373         "252"
43374       ],
43375       [
43376         "South Africa",
43377         "za",
43378         "27"
43379       ],
43380       [
43381         "South Korea (대한민국)",
43382         "kr",
43383         "82"
43384       ],
43385       [
43386         "South Sudan (‫جنوب السودان‬‎)",
43387         "ss",
43388         "211"
43389       ],
43390       [
43391         "Spain (España)",
43392         "es",
43393         "34"
43394       ],
43395       [
43396         "Sri Lanka (ශ්‍රී ලංකාව)",
43397         "lk",
43398         "94"
43399       ],
43400       [
43401         "Sudan (‫السودان‬‎)",
43402         "sd",
43403         "249"
43404       ],
43405       [
43406         "Suriname",
43407         "sr",
43408         "597"
43409       ],
43410       [
43411         "Svalbard and Jan Mayen",
43412         "sj",
43413         "47",
43414         1
43415       ],
43416       [
43417         "Swaziland",
43418         "sz",
43419         "268"
43420       ],
43421       [
43422         "Sweden (Sverige)",
43423         "se",
43424         "46"
43425       ],
43426       [
43427         "Switzerland (Schweiz)",
43428         "ch",
43429         "41"
43430       ],
43431       [
43432         "Syria (‫سوريا‬‎)",
43433         "sy",
43434         "963"
43435       ],
43436       [
43437         "Taiwan (台灣)",
43438         "tw",
43439         "886"
43440       ],
43441       [
43442         "Tajikistan",
43443         "tj",
43444         "992"
43445       ],
43446       [
43447         "Tanzania",
43448         "tz",
43449         "255"
43450       ],
43451       [
43452         "Thailand (ไทย)",
43453         "th",
43454         "66"
43455       ],
43456       [
43457         "Timor-Leste",
43458         "tl",
43459         "670"
43460       ],
43461       [
43462         "Togo",
43463         "tg",
43464         "228"
43465       ],
43466       [
43467         "Tokelau",
43468         "tk",
43469         "690"
43470       ],
43471       [
43472         "Tonga",
43473         "to",
43474         "676"
43475       ],
43476       [
43477         "Trinidad and Tobago",
43478         "tt",
43479         "1868"
43480       ],
43481       [
43482         "Tunisia (‫تونس‬‎)",
43483         "tn",
43484         "216"
43485       ],
43486       [
43487         "Turkey (Türkiye)",
43488         "tr",
43489         "90"
43490       ],
43491       [
43492         "Turkmenistan",
43493         "tm",
43494         "993"
43495       ],
43496       [
43497         "Turks and Caicos Islands",
43498         "tc",
43499         "1649"
43500       ],
43501       [
43502         "Tuvalu",
43503         "tv",
43504         "688"
43505       ],
43506       [
43507         "U.S. Virgin Islands",
43508         "vi",
43509         "1340"
43510       ],
43511       [
43512         "Uganda",
43513         "ug",
43514         "256"
43515       ],
43516       [
43517         "Ukraine (Україна)",
43518         "ua",
43519         "380"
43520       ],
43521       [
43522         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43523         "ae",
43524         "971"
43525       ],
43526       [
43527         "United Kingdom",
43528         "gb",
43529         "44",
43530         0
43531       ],
43532       [
43533         "United States",
43534         "us",
43535         "1",
43536         0
43537       ],
43538       [
43539         "Uruguay",
43540         "uy",
43541         "598"
43542       ],
43543       [
43544         "Uzbekistan (Oʻzbekiston)",
43545         "uz",
43546         "998"
43547       ],
43548       [
43549         "Vanuatu",
43550         "vu",
43551         "678"
43552       ],
43553       [
43554         "Vatican City (Città del Vaticano)",
43555         "va",
43556         "39",
43557         1
43558       ],
43559       [
43560         "Venezuela",
43561         "ve",
43562         "58"
43563       ],
43564       [
43565         "Vietnam (Việt Nam)",
43566         "vn",
43567         "84"
43568       ],
43569       [
43570         "Wallis and Futuna (Wallis-et-Futuna)",
43571         "wf",
43572         "681"
43573       ],
43574       [
43575         "Western Sahara (‫الصحراء الغربية‬‎)",
43576         "eh",
43577         "212",
43578         1
43579       ],
43580       [
43581         "Yemen (‫اليمن‬‎)",
43582         "ye",
43583         "967"
43584       ],
43585       [
43586         "Zambia",
43587         "zm",
43588         "260"
43589       ],
43590       [
43591         "Zimbabwe",
43592         "zw",
43593         "263"
43594       ],
43595       [
43596         "Åland Islands",
43597         "ax",
43598         "358",
43599         1
43600       ]
43601   ];
43602   
43603   return d;
43604 }/**
43605 *    This script refer to:
43606 *    Title: International Telephone Input
43607 *    Author: Jack O'Connor
43608 *    Code version:  v12.1.12
43609 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43610 **/
43611
43612 /**
43613  * @class Roo.bootstrap.PhoneInput
43614  * @extends Roo.bootstrap.TriggerField
43615  * An input with International dial-code selection
43616  
43617  * @cfg {String} defaultDialCode default '+852'
43618  * @cfg {Array} preferedCountries default []
43619   
43620  * @constructor
43621  * Create a new PhoneInput.
43622  * @param {Object} config Configuration options
43623  */
43624
43625 Roo.bootstrap.PhoneInput = function(config) {
43626     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43627 };
43628
43629 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43630         
43631         listWidth: undefined,
43632         
43633         selectedClass: 'active',
43634         
43635         invalidClass : "has-warning",
43636         
43637         validClass: 'has-success',
43638         
43639         allowed: '0123456789',
43640         
43641         max_length: 15,
43642         
43643         /**
43644          * @cfg {String} defaultDialCode The default dial code when initializing the input
43645          */
43646         defaultDialCode: '+852',
43647         
43648         /**
43649          * @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
43650          */
43651         preferedCountries: false,
43652         
43653         getAutoCreate : function()
43654         {
43655             var data = Roo.bootstrap.PhoneInputData();
43656             var align = this.labelAlign || this.parentLabelAlign();
43657             var id = Roo.id();
43658             
43659             this.allCountries = [];
43660             this.dialCodeMapping = [];
43661             
43662             for (var i = 0; i < data.length; i++) {
43663               var c = data[i];
43664               this.allCountries[i] = {
43665                 name: c[0],
43666                 iso2: c[1],
43667                 dialCode: c[2],
43668                 priority: c[3] || 0,
43669                 areaCodes: c[4] || null
43670               };
43671               this.dialCodeMapping[c[2]] = {
43672                   name: c[0],
43673                   iso2: c[1],
43674                   priority: c[3] || 0,
43675                   areaCodes: c[4] || null
43676               };
43677             }
43678             
43679             var cfg = {
43680                 cls: 'form-group',
43681                 cn: []
43682             };
43683             
43684             var input =  {
43685                 tag: 'input',
43686                 id : id,
43687                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43688                 maxlength: this.max_length,
43689                 cls : 'form-control tel-input',
43690                 autocomplete: 'new-password'
43691             };
43692             
43693             var hiddenInput = {
43694                 tag: 'input',
43695                 type: 'hidden',
43696                 cls: 'hidden-tel-input'
43697             };
43698             
43699             if (this.name) {
43700                 hiddenInput.name = this.name;
43701             }
43702             
43703             if (this.disabled) {
43704                 input.disabled = true;
43705             }
43706             
43707             var flag_container = {
43708                 tag: 'div',
43709                 cls: 'flag-box',
43710                 cn: [
43711                     {
43712                         tag: 'div',
43713                         cls: 'flag'
43714                     },
43715                     {
43716                         tag: 'div',
43717                         cls: 'caret'
43718                     }
43719                 ]
43720             };
43721             
43722             var box = {
43723                 tag: 'div',
43724                 cls: this.hasFeedback ? 'has-feedback' : '',
43725                 cn: [
43726                     hiddenInput,
43727                     input,
43728                     {
43729                         tag: 'input',
43730                         cls: 'dial-code-holder',
43731                         disabled: true
43732                     }
43733                 ]
43734             };
43735             
43736             var container = {
43737                 cls: 'roo-select2-container input-group',
43738                 cn: [
43739                     flag_container,
43740                     box
43741                 ]
43742             };
43743             
43744             if (this.fieldLabel.length) {
43745                 var indicator = {
43746                     tag: 'i',
43747                     tooltip: 'This field is required'
43748                 };
43749                 
43750                 var label = {
43751                     tag: 'label',
43752                     'for':  id,
43753                     cls: 'control-label',
43754                     cn: []
43755                 };
43756                 
43757                 var label_text = {
43758                     tag: 'span',
43759                     html: this.fieldLabel
43760                 };
43761                 
43762                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43763                 label.cn = [
43764                     indicator,
43765                     label_text
43766                 ];
43767                 
43768                 if(this.indicatorpos == 'right') {
43769                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43770                     label.cn = [
43771                         label_text,
43772                         indicator
43773                     ];
43774                 }
43775                 
43776                 if(align == 'left') {
43777                     container = {
43778                         tag: 'div',
43779                         cn: [
43780                             container
43781                         ]
43782                     };
43783                     
43784                     if(this.labelWidth > 12){
43785                         label.style = "width: " + this.labelWidth + 'px';
43786                     }
43787                     if(this.labelWidth < 13 && this.labelmd == 0){
43788                         this.labelmd = this.labelWidth;
43789                     }
43790                     if(this.labellg > 0){
43791                         label.cls += ' col-lg-' + this.labellg;
43792                         input.cls += ' col-lg-' + (12 - this.labellg);
43793                     }
43794                     if(this.labelmd > 0){
43795                         label.cls += ' col-md-' + this.labelmd;
43796                         container.cls += ' col-md-' + (12 - this.labelmd);
43797                     }
43798                     if(this.labelsm > 0){
43799                         label.cls += ' col-sm-' + this.labelsm;
43800                         container.cls += ' col-sm-' + (12 - this.labelsm);
43801                     }
43802                     if(this.labelxs > 0){
43803                         label.cls += ' col-xs-' + this.labelxs;
43804                         container.cls += ' col-xs-' + (12 - this.labelxs);
43805                     }
43806                 }
43807             }
43808             
43809             cfg.cn = [
43810                 label,
43811                 container
43812             ];
43813             
43814             var settings = this;
43815             
43816             ['xs','sm','md','lg'].map(function(size){
43817                 if (settings[size]) {
43818                     cfg.cls += ' col-' + size + '-' + settings[size];
43819                 }
43820             });
43821             
43822             this.store = new Roo.data.Store({
43823                 proxy : new Roo.data.MemoryProxy({}),
43824                 reader : new Roo.data.JsonReader({
43825                     fields : [
43826                         {
43827                             'name' : 'name',
43828                             'type' : 'string'
43829                         },
43830                         {
43831                             'name' : 'iso2',
43832                             'type' : 'string'
43833                         },
43834                         {
43835                             'name' : 'dialCode',
43836                             'type' : 'string'
43837                         },
43838                         {
43839                             'name' : 'priority',
43840                             'type' : 'string'
43841                         },
43842                         {
43843                             'name' : 'areaCodes',
43844                             'type' : 'string'
43845                         }
43846                     ]
43847                 })
43848             });
43849             
43850             if(!this.preferedCountries) {
43851                 this.preferedCountries = [
43852                     'hk',
43853                     'gb',
43854                     'us'
43855                 ];
43856             }
43857             
43858             var p = this.preferedCountries.reverse();
43859             
43860             if(p) {
43861                 for (var i = 0; i < p.length; i++) {
43862                     for (var j = 0; j < this.allCountries.length; j++) {
43863                         if(this.allCountries[j].iso2 == p[i]) {
43864                             var t = this.allCountries[j];
43865                             this.allCountries.splice(j,1);
43866                             this.allCountries.unshift(t);
43867                         }
43868                     } 
43869                 }
43870             }
43871             
43872             this.store.proxy.data = {
43873                 success: true,
43874                 data: this.allCountries
43875             };
43876             
43877             return cfg;
43878         },
43879         
43880         initEvents : function()
43881         {
43882             this.createList();
43883             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43884             
43885             this.indicator = this.indicatorEl();
43886             this.flag = this.flagEl();
43887             this.dialCodeHolder = this.dialCodeHolderEl();
43888             
43889             this.trigger = this.el.select('div.flag-box',true).first();
43890             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43891             
43892             var _this = this;
43893             
43894             (function(){
43895                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43896                 _this.list.setWidth(lw);
43897             }).defer(100);
43898             
43899             this.list.on('mouseover', this.onViewOver, this);
43900             this.list.on('mousemove', this.onViewMove, this);
43901             this.inputEl().on("keyup", this.onKeyUp, this);
43902             this.inputEl().on("keypress", this.onKeyPress, this);
43903             
43904             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43905
43906             this.view = new Roo.View(this.list, this.tpl, {
43907                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43908             });
43909             
43910             this.view.on('click', this.onViewClick, this);
43911             this.setValue(this.defaultDialCode);
43912         },
43913         
43914         onTriggerClick : function(e)
43915         {
43916             Roo.log('trigger click');
43917             if(this.disabled){
43918                 return;
43919             }
43920             
43921             if(this.isExpanded()){
43922                 this.collapse();
43923                 this.hasFocus = false;
43924             }else {
43925                 this.store.load({});
43926                 this.hasFocus = true;
43927                 this.expand();
43928             }
43929         },
43930         
43931         isExpanded : function()
43932         {
43933             return this.list.isVisible();
43934         },
43935         
43936         collapse : function()
43937         {
43938             if(!this.isExpanded()){
43939                 return;
43940             }
43941             this.list.hide();
43942             Roo.get(document).un('mousedown', this.collapseIf, this);
43943             Roo.get(document).un('mousewheel', this.collapseIf, this);
43944             this.fireEvent('collapse', this);
43945             this.validate();
43946         },
43947         
43948         expand : function()
43949         {
43950             Roo.log('expand');
43951
43952             if(this.isExpanded() || !this.hasFocus){
43953                 return;
43954             }
43955             
43956             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43957             this.list.setWidth(lw);
43958             
43959             this.list.show();
43960             this.restrictHeight();
43961             
43962             Roo.get(document).on('mousedown', this.collapseIf, this);
43963             Roo.get(document).on('mousewheel', this.collapseIf, this);
43964             
43965             this.fireEvent('expand', this);
43966         },
43967         
43968         restrictHeight : function()
43969         {
43970             this.list.alignTo(this.inputEl(), this.listAlign);
43971             this.list.alignTo(this.inputEl(), this.listAlign);
43972         },
43973         
43974         onViewOver : function(e, t)
43975         {
43976             if(this.inKeyMode){
43977                 return;
43978             }
43979             var item = this.view.findItemFromChild(t);
43980             
43981             if(item){
43982                 var index = this.view.indexOf(item);
43983                 this.select(index, false);
43984             }
43985         },
43986
43987         // private
43988         onViewClick : function(view, doFocus, el, e)
43989         {
43990             var index = this.view.getSelectedIndexes()[0];
43991             
43992             var r = this.store.getAt(index);
43993             
43994             if(r){
43995                 this.onSelect(r, index);
43996             }
43997             if(doFocus !== false && !this.blockFocus){
43998                 this.inputEl().focus();
43999             }
44000         },
44001         
44002         onViewMove : function(e, t)
44003         {
44004             this.inKeyMode = false;
44005         },
44006         
44007         select : function(index, scrollIntoView)
44008         {
44009             this.selectedIndex = index;
44010             this.view.select(index);
44011             if(scrollIntoView !== false){
44012                 var el = this.view.getNode(index);
44013                 if(el){
44014                     this.list.scrollChildIntoView(el, false);
44015                 }
44016             }
44017         },
44018         
44019         createList : function()
44020         {
44021             this.list = Roo.get(document.body).createChild({
44022                 tag: 'ul',
44023                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44024                 style: 'display:none'
44025             });
44026             
44027             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44028         },
44029         
44030         collapseIf : function(e)
44031         {
44032             var in_combo  = e.within(this.el);
44033             var in_list =  e.within(this.list);
44034             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44035             
44036             if (in_combo || in_list || is_list) {
44037                 return;
44038             }
44039             this.collapse();
44040         },
44041         
44042         onSelect : function(record, index)
44043         {
44044             if(this.fireEvent('beforeselect', this, record, index) !== false){
44045                 
44046                 this.setFlagClass(record.data.iso2);
44047                 this.setDialCode(record.data.dialCode);
44048                 this.hasFocus = false;
44049                 this.collapse();
44050                 this.fireEvent('select', this, record, index);
44051             }
44052         },
44053         
44054         flagEl : function()
44055         {
44056             var flag = this.el.select('div.flag',true).first();
44057             if(!flag){
44058                 return false;
44059             }
44060             return flag;
44061         },
44062         
44063         dialCodeHolderEl : function()
44064         {
44065             var d = this.el.select('input.dial-code-holder',true).first();
44066             if(!d){
44067                 return false;
44068             }
44069             return d;
44070         },
44071         
44072         setDialCode : function(v)
44073         {
44074             this.dialCodeHolder.dom.value = '+'+v;
44075         },
44076         
44077         setFlagClass : function(n)
44078         {
44079             this.flag.dom.className = 'flag '+n;
44080         },
44081         
44082         getValue : function()
44083         {
44084             var v = this.inputEl().getValue();
44085             if(this.dialCodeHolder) {
44086                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44087             }
44088             return v;
44089         },
44090         
44091         setValue : function(v)
44092         {
44093             var d = this.getDialCode(v);
44094             
44095             //invalid dial code
44096             if(v.length == 0 || !d || d.length == 0) {
44097                 if(this.rendered){
44098                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44099                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44100                 }
44101                 return;
44102             }
44103             
44104             //valid dial code
44105             this.setFlagClass(this.dialCodeMapping[d].iso2);
44106             this.setDialCode(d);
44107             this.inputEl().dom.value = v.replace('+'+d,'');
44108             this.hiddenEl().dom.value = this.getValue();
44109             
44110             this.validate();
44111         },
44112         
44113         getDialCode : function(v)
44114         {
44115             v = v ||  '';
44116             
44117             if (v.length == 0) {
44118                 return this.dialCodeHolder.dom.value;
44119             }
44120             
44121             var dialCode = "";
44122             if (v.charAt(0) != "+") {
44123                 return false;
44124             }
44125             var numericChars = "";
44126             for (var i = 1; i < v.length; i++) {
44127               var c = v.charAt(i);
44128               if (!isNaN(c)) {
44129                 numericChars += c;
44130                 if (this.dialCodeMapping[numericChars]) {
44131                   dialCode = v.substr(1, i);
44132                 }
44133                 if (numericChars.length == 4) {
44134                   break;
44135                 }
44136               }
44137             }
44138             return dialCode;
44139         },
44140         
44141         reset : function()
44142         {
44143             this.setValue(this.defaultDialCode);
44144             this.validate();
44145         },
44146         
44147         hiddenEl : function()
44148         {
44149             return this.el.select('input.hidden-tel-input',true).first();
44150         },
44151         
44152         // after setting val
44153         onKeyUp : function(e){
44154             this.setValue(this.getValue());
44155         },
44156         
44157         onKeyPress : function(e){
44158             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44159                 e.stopEvent();
44160             }
44161         }
44162         
44163 });
44164 /**
44165  * @class Roo.bootstrap.MoneyField
44166  * @extends Roo.bootstrap.ComboBox
44167  * Bootstrap MoneyField class
44168  * 
44169  * @constructor
44170  * Create a new MoneyField.
44171  * @param {Object} config Configuration options
44172  */
44173
44174 Roo.bootstrap.MoneyField = function(config) {
44175     
44176     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44177     
44178 };
44179
44180 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44181     
44182     /**
44183      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44184      */
44185     allowDecimals : true,
44186     /**
44187      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44188      */
44189     decimalSeparator : ".",
44190     /**
44191      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44192      */
44193     decimalPrecision : 0,
44194     /**
44195      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44196      */
44197     allowNegative : true,
44198     /**
44199      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44200      */
44201     allowZero: true,
44202     /**
44203      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44204      */
44205     minValue : Number.NEGATIVE_INFINITY,
44206     /**
44207      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44208      */
44209     maxValue : Number.MAX_VALUE,
44210     /**
44211      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44212      */
44213     minText : "The minimum value for this field is {0}",
44214     /**
44215      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44216      */
44217     maxText : "The maximum value for this field is {0}",
44218     /**
44219      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44220      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44221      */
44222     nanText : "{0} is not a valid number",
44223     /**
44224      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44225      */
44226     castInt : true,
44227     /**
44228      * @cfg {String} defaults currency of the MoneyField
44229      * value should be in lkey
44230      */
44231     defaultCurrency : false,
44232     /**
44233      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44234      */
44235     thousandsDelimiter : false,
44236     /**
44237      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44238      */
44239     max_length: false,
44240     
44241     inputlg : 9,
44242     inputmd : 9,
44243     inputsm : 9,
44244     inputxs : 6,
44245     
44246     store : false,
44247     
44248     getAutoCreate : function()
44249     {
44250         var align = this.labelAlign || this.parentLabelAlign();
44251         
44252         var id = Roo.id();
44253
44254         var cfg = {
44255             cls: 'form-group',
44256             cn: []
44257         };
44258
44259         var input =  {
44260             tag: 'input',
44261             id : id,
44262             cls : 'form-control roo-money-amount-input',
44263             autocomplete: 'new-password'
44264         };
44265         
44266         var hiddenInput = {
44267             tag: 'input',
44268             type: 'hidden',
44269             id: Roo.id(),
44270             cls: 'hidden-number-input'
44271         };
44272         
44273         if(this.max_length) {
44274             input.maxlength = this.max_length; 
44275         }
44276         
44277         if (this.name) {
44278             hiddenInput.name = this.name;
44279         }
44280
44281         if (this.disabled) {
44282             input.disabled = true;
44283         }
44284
44285         var clg = 12 - this.inputlg;
44286         var cmd = 12 - this.inputmd;
44287         var csm = 12 - this.inputsm;
44288         var cxs = 12 - this.inputxs;
44289         
44290         var container = {
44291             tag : 'div',
44292             cls : 'row roo-money-field',
44293             cn : [
44294                 {
44295                     tag : 'div',
44296                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44297                     cn : [
44298                         {
44299                             tag : 'div',
44300                             cls: 'roo-select2-container input-group',
44301                             cn: [
44302                                 {
44303                                     tag : 'input',
44304                                     cls : 'form-control roo-money-currency-input',
44305                                     autocomplete: 'new-password',
44306                                     readOnly : 1,
44307                                     name : this.currencyName
44308                                 },
44309                                 {
44310                                     tag :'span',
44311                                     cls : 'input-group-addon',
44312                                     cn : [
44313                                         {
44314                                             tag: 'span',
44315                                             cls: 'caret'
44316                                         }
44317                                     ]
44318                                 }
44319                             ]
44320                         }
44321                     ]
44322                 },
44323                 {
44324                     tag : 'div',
44325                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44326                     cn : [
44327                         {
44328                             tag: 'div',
44329                             cls: this.hasFeedback ? 'has-feedback' : '',
44330                             cn: [
44331                                 input
44332                             ]
44333                         }
44334                     ]
44335                 }
44336             ]
44337             
44338         };
44339         
44340         if (this.fieldLabel.length) {
44341             var indicator = {
44342                 tag: 'i',
44343                 tooltip: 'This field is required'
44344             };
44345
44346             var label = {
44347                 tag: 'label',
44348                 'for':  id,
44349                 cls: 'control-label',
44350                 cn: []
44351             };
44352
44353             var label_text = {
44354                 tag: 'span',
44355                 html: this.fieldLabel
44356             };
44357
44358             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44359             label.cn = [
44360                 indicator,
44361                 label_text
44362             ];
44363
44364             if(this.indicatorpos == 'right') {
44365                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44366                 label.cn = [
44367                     label_text,
44368                     indicator
44369                 ];
44370             }
44371
44372             if(align == 'left') {
44373                 container = {
44374                     tag: 'div',
44375                     cn: [
44376                         container
44377                     ]
44378                 };
44379
44380                 if(this.labelWidth > 12){
44381                     label.style = "width: " + this.labelWidth + 'px';
44382                 }
44383                 if(this.labelWidth < 13 && this.labelmd == 0){
44384                     this.labelmd = this.labelWidth;
44385                 }
44386                 if(this.labellg > 0){
44387                     label.cls += ' col-lg-' + this.labellg;
44388                     input.cls += ' col-lg-' + (12 - this.labellg);
44389                 }
44390                 if(this.labelmd > 0){
44391                     label.cls += ' col-md-' + this.labelmd;
44392                     container.cls += ' col-md-' + (12 - this.labelmd);
44393                 }
44394                 if(this.labelsm > 0){
44395                     label.cls += ' col-sm-' + this.labelsm;
44396                     container.cls += ' col-sm-' + (12 - this.labelsm);
44397                 }
44398                 if(this.labelxs > 0){
44399                     label.cls += ' col-xs-' + this.labelxs;
44400                     container.cls += ' col-xs-' + (12 - this.labelxs);
44401                 }
44402             }
44403         }
44404
44405         cfg.cn = [
44406             label,
44407             container,
44408             hiddenInput
44409         ];
44410         
44411         var settings = this;
44412
44413         ['xs','sm','md','lg'].map(function(size){
44414             if (settings[size]) {
44415                 cfg.cls += ' col-' + size + '-' + settings[size];
44416             }
44417         });
44418         
44419         return cfg;
44420     },
44421     
44422     initEvents : function()
44423     {
44424         this.indicator = this.indicatorEl();
44425         
44426         this.initCurrencyEvent();
44427         
44428         this.initNumberEvent();
44429     },
44430     
44431     initCurrencyEvent : function()
44432     {
44433         if (!this.store) {
44434             throw "can not find store for combo";
44435         }
44436         
44437         this.store = Roo.factory(this.store, Roo.data);
44438         this.store.parent = this;
44439         
44440         this.createList();
44441         
44442         this.triggerEl = this.el.select('.input-group-addon', true).first();
44443         
44444         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44445         
44446         var _this = this;
44447         
44448         (function(){
44449             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44450             _this.list.setWidth(lw);
44451         }).defer(100);
44452         
44453         this.list.on('mouseover', this.onViewOver, this);
44454         this.list.on('mousemove', this.onViewMove, this);
44455         this.list.on('scroll', this.onViewScroll, this);
44456         
44457         if(!this.tpl){
44458             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44459         }
44460         
44461         this.view = new Roo.View(this.list, this.tpl, {
44462             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44463         });
44464         
44465         this.view.on('click', this.onViewClick, this);
44466         
44467         this.store.on('beforeload', this.onBeforeLoad, this);
44468         this.store.on('load', this.onLoad, this);
44469         this.store.on('loadexception', this.onLoadException, this);
44470         
44471         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44472             "up" : function(e){
44473                 this.inKeyMode = true;
44474                 this.selectPrev();
44475             },
44476
44477             "down" : function(e){
44478                 if(!this.isExpanded()){
44479                     this.onTriggerClick();
44480                 }else{
44481                     this.inKeyMode = true;
44482                     this.selectNext();
44483                 }
44484             },
44485
44486             "enter" : function(e){
44487                 this.collapse();
44488                 
44489                 if(this.fireEvent("specialkey", this, e)){
44490                     this.onViewClick(false);
44491                 }
44492                 
44493                 return true;
44494             },
44495
44496             "esc" : function(e){
44497                 this.collapse();
44498             },
44499
44500             "tab" : function(e){
44501                 this.collapse();
44502                 
44503                 if(this.fireEvent("specialkey", this, e)){
44504                     this.onViewClick(false);
44505                 }
44506                 
44507                 return true;
44508             },
44509
44510             scope : this,
44511
44512             doRelay : function(foo, bar, hname){
44513                 if(hname == 'down' || this.scope.isExpanded()){
44514                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44515                 }
44516                 return true;
44517             },
44518
44519             forceKeyDown: true
44520         });
44521         
44522         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44523         
44524     },
44525     
44526     initNumberEvent : function(e)
44527     {
44528         this.inputEl().on("keydown" , this.fireKey,  this);
44529         this.inputEl().on("focus", this.onFocus,  this);
44530         this.inputEl().on("blur", this.onBlur,  this);
44531         
44532         this.inputEl().relayEvent('keyup', this);
44533         
44534         if(this.indicator){
44535             this.indicator.addClass('invisible');
44536         }
44537  
44538         this.originalValue = this.getValue();
44539         
44540         if(this.validationEvent == 'keyup'){
44541             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44542             this.inputEl().on('keyup', this.filterValidation, this);
44543         }
44544         else if(this.validationEvent !== false){
44545             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44546         }
44547         
44548         if(this.selectOnFocus){
44549             this.on("focus", this.preFocus, this);
44550             
44551         }
44552         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44553             this.inputEl().on("keypress", this.filterKeys, this);
44554         } else {
44555             this.inputEl().relayEvent('keypress', this);
44556         }
44557         
44558         var allowed = "0123456789";
44559         
44560         if(this.allowDecimals){
44561             allowed += this.decimalSeparator;
44562         }
44563         
44564         if(this.allowNegative){
44565             allowed += "-";
44566         }
44567         
44568         if(this.thousandsDelimiter) {
44569             allowed += ",";
44570         }
44571         
44572         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44573         
44574         var keyPress = function(e){
44575             
44576             var k = e.getKey();
44577             
44578             var c = e.getCharCode();
44579             
44580             if(
44581                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44582                     allowed.indexOf(String.fromCharCode(c)) === -1
44583             ){
44584                 e.stopEvent();
44585                 return;
44586             }
44587             
44588             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44589                 return;
44590             }
44591             
44592             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44593                 e.stopEvent();
44594             }
44595         };
44596         
44597         this.inputEl().on("keypress", keyPress, this);
44598         
44599     },
44600     
44601     onTriggerClick : function(e)
44602     {   
44603         if(this.disabled){
44604             return;
44605         }
44606         
44607         this.page = 0;
44608         this.loadNext = false;
44609         
44610         if(this.isExpanded()){
44611             this.collapse();
44612             return;
44613         }
44614         
44615         this.hasFocus = true;
44616         
44617         if(this.triggerAction == 'all') {
44618             this.doQuery(this.allQuery, true);
44619             return;
44620         }
44621         
44622         this.doQuery(this.getRawValue());
44623     },
44624     
44625     getCurrency : function()
44626     {   
44627         var v = this.currencyEl().getValue();
44628         
44629         return v;
44630     },
44631     
44632     restrictHeight : function()
44633     {
44634         this.list.alignTo(this.currencyEl(), this.listAlign);
44635         this.list.alignTo(this.currencyEl(), this.listAlign);
44636     },
44637     
44638     onViewClick : function(view, doFocus, el, e)
44639     {
44640         var index = this.view.getSelectedIndexes()[0];
44641         
44642         var r = this.store.getAt(index);
44643         
44644         if(r){
44645             this.onSelect(r, index);
44646         }
44647     },
44648     
44649     onSelect : function(record, index){
44650         
44651         if(this.fireEvent('beforeselect', this, record, index) !== false){
44652         
44653             this.setFromCurrencyData(index > -1 ? record.data : false);
44654             
44655             this.collapse();
44656             
44657             this.fireEvent('select', this, record, index);
44658         }
44659     },
44660     
44661     setFromCurrencyData : function(o)
44662     {
44663         var currency = '';
44664         
44665         this.lastCurrency = o;
44666         
44667         if (this.currencyField) {
44668             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44669         } else {
44670             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44671         }
44672         
44673         this.lastSelectionText = currency;
44674         
44675         //setting default currency
44676         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44677             this.setCurrency(this.defaultCurrency);
44678             return;
44679         }
44680         
44681         this.setCurrency(currency);
44682     },
44683     
44684     setFromData : function(o)
44685     {
44686         var c = {};
44687         
44688         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44689         
44690         this.setFromCurrencyData(c);
44691         
44692         var value = '';
44693         
44694         if (this.name) {
44695             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44696         } else {
44697             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44698         }
44699         
44700         this.setValue(value);
44701         
44702     },
44703     
44704     setCurrency : function(v)
44705     {   
44706         this.currencyValue = v;
44707         
44708         if(this.rendered){
44709             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44710             this.validate();
44711         }
44712     },
44713     
44714     setValue : function(v)
44715     {
44716         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44717         
44718         this.value = v;
44719         
44720         if(this.rendered){
44721             
44722             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44723             
44724             this.inputEl().dom.value = (v == '') ? '' :
44725                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44726             
44727             if(!this.allowZero && v === '0') {
44728                 this.hiddenEl().dom.value = '';
44729                 this.inputEl().dom.value = '';
44730             }
44731             
44732             this.validate();
44733         }
44734     },
44735     
44736     getRawValue : function()
44737     {
44738         var v = this.inputEl().getValue();
44739         
44740         return v;
44741     },
44742     
44743     getValue : function()
44744     {
44745         return this.fixPrecision(this.parseValue(this.getRawValue()));
44746     },
44747     
44748     parseValue : function(value)
44749     {
44750         if(this.thousandsDelimiter) {
44751             value += "";
44752             r = new RegExp(",", "g");
44753             value = value.replace(r, "");
44754         }
44755         
44756         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44757         return isNaN(value) ? '' : value;
44758         
44759     },
44760     
44761     fixPrecision : function(value)
44762     {
44763         if(this.thousandsDelimiter) {
44764             value += "";
44765             r = new RegExp(",", "g");
44766             value = value.replace(r, "");
44767         }
44768         
44769         var nan = isNaN(value);
44770         
44771         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44772             return nan ? '' : value;
44773         }
44774         return parseFloat(value).toFixed(this.decimalPrecision);
44775     },
44776     
44777     decimalPrecisionFcn : function(v)
44778     {
44779         return Math.floor(v);
44780     },
44781     
44782     validateValue : function(value)
44783     {
44784         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44785             return false;
44786         }
44787         
44788         var num = this.parseValue(value);
44789         
44790         if(isNaN(num)){
44791             this.markInvalid(String.format(this.nanText, value));
44792             return false;
44793         }
44794         
44795         if(num < this.minValue){
44796             this.markInvalid(String.format(this.minText, this.minValue));
44797             return false;
44798         }
44799         
44800         if(num > this.maxValue){
44801             this.markInvalid(String.format(this.maxText, this.maxValue));
44802             return false;
44803         }
44804         
44805         return true;
44806     },
44807     
44808     validate : function()
44809     {
44810         if(this.disabled || this.allowBlank){
44811             this.markValid();
44812             return true;
44813         }
44814         
44815         var currency = this.getCurrency();
44816         
44817         if(this.validateValue(this.getRawValue()) && currency.length){
44818             this.markValid();
44819             return true;
44820         }
44821         
44822         this.markInvalid();
44823         return false;
44824     },
44825     
44826     getName: function()
44827     {
44828         return this.name;
44829     },
44830     
44831     beforeBlur : function()
44832     {
44833         if(!this.castInt){
44834             return;
44835         }
44836         
44837         var v = this.parseValue(this.getRawValue());
44838         
44839         if(v || v == 0){
44840             this.setValue(v);
44841         }
44842     },
44843     
44844     onBlur : function()
44845     {
44846         this.beforeBlur();
44847         
44848         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44849             //this.el.removeClass(this.focusClass);
44850         }
44851         
44852         this.hasFocus = false;
44853         
44854         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44855             this.validate();
44856         }
44857         
44858         var v = this.getValue();
44859         
44860         if(String(v) !== String(this.startValue)){
44861             this.fireEvent('change', this, v, this.startValue);
44862         }
44863         
44864         this.fireEvent("blur", this);
44865     },
44866     
44867     inputEl : function()
44868     {
44869         return this.el.select('.roo-money-amount-input', true).first();
44870     },
44871     
44872     currencyEl : function()
44873     {
44874         return this.el.select('.roo-money-currency-input', true).first();
44875     },
44876     
44877     hiddenEl : function()
44878     {
44879         return this.el.select('input.hidden-number-input',true).first();
44880     }
44881     
44882 });/**
44883  * @class Roo.bootstrap.BezierSignature
44884  * @extends Roo.bootstrap.Component
44885  * Bootstrap BezierSignature class
44886  * This script refer to:
44887  *    Title: Signature Pad
44888  *    Author: szimek
44889  *    Availability: https://github.com/szimek/signature_pad
44890  *
44891  * @constructor
44892  * Create a new BezierSignature
44893  * @param {Object} config The config object
44894  */
44895
44896 Roo.bootstrap.BezierSignature = function(config){
44897     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44898     this.addEvents({
44899         "resize" : true
44900     });
44901 };
44902
44903 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44904 {
44905      
44906     curve_data: [],
44907     
44908     is_empty: true,
44909     
44910     mouse_btn_down: true,
44911     
44912     /**
44913      * @cfg {int} canvas height
44914      */
44915     canvas_height: '200px',
44916     
44917     /**
44918      * @cfg {float|function} Radius of a single dot.
44919      */ 
44920     dot_size: false,
44921     
44922     /**
44923      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44924      */
44925     min_width: 0.5,
44926     
44927     /**
44928      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44929      */
44930     max_width: 2.5,
44931     
44932     /**
44933      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44934      */
44935     throttle: 16,
44936     
44937     /**
44938      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44939      */
44940     min_distance: 5,
44941     
44942     /**
44943      * @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.
44944      */
44945     bg_color: 'rgba(0, 0, 0, 0)',
44946     
44947     /**
44948      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44949      */
44950     dot_color: 'black',
44951     
44952     /**
44953      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44954      */ 
44955     velocity_filter_weight: 0.7,
44956     
44957     /**
44958      * @cfg {function} Callback when stroke begin. 
44959      */
44960     onBegin: false,
44961     
44962     /**
44963      * @cfg {function} Callback when stroke end.
44964      */
44965     onEnd: false,
44966     
44967     getAutoCreate : function()
44968     {
44969         var cls = 'roo-signature column';
44970         
44971         if(this.cls){
44972             cls += ' ' + this.cls;
44973         }
44974         
44975         var col_sizes = [
44976             'lg',
44977             'md',
44978             'sm',
44979             'xs'
44980         ];
44981         
44982         for(var i = 0; i < col_sizes.length; i++) {
44983             if(this[col_sizes[i]]) {
44984                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44985             }
44986         }
44987         
44988         var cfg = {
44989             tag: 'div',
44990             cls: cls,
44991             cn: [
44992                 {
44993                     tag: 'div',
44994                     cls: 'roo-signature-body',
44995                     cn: [
44996                         {
44997                             tag: 'canvas',
44998                             cls: 'roo-signature-body-canvas',
44999                             height: this.canvas_height,
45000                             width: this.canvas_width
45001                         }
45002                     ]
45003                 },
45004                 {
45005                     tag: 'input',
45006                     type: 'file',
45007                     style: 'display: none'
45008                 }
45009             ]
45010         };
45011         
45012         return cfg;
45013     },
45014     
45015     initEvents: function() 
45016     {
45017         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45018         
45019         var canvas = this.canvasEl();
45020         
45021         // mouse && touch event swapping...
45022         canvas.dom.style.touchAction = 'none';
45023         canvas.dom.style.msTouchAction = 'none';
45024         
45025         this.mouse_btn_down = false;
45026         canvas.on('mousedown', this._handleMouseDown, this);
45027         canvas.on('mousemove', this._handleMouseMove, this);
45028         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45029         
45030         if (window.PointerEvent) {
45031             canvas.on('pointerdown', this._handleMouseDown, this);
45032             canvas.on('pointermove', this._handleMouseMove, this);
45033             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45034         }
45035         
45036         if ('ontouchstart' in window) {
45037             canvas.on('touchstart', this._handleTouchStart, this);
45038             canvas.on('touchmove', this._handleTouchMove, this);
45039             canvas.on('touchend', this._handleTouchEnd, this);
45040         }
45041         
45042         Roo.EventManager.onWindowResize(this.resize, this, true);
45043         
45044         // file input event
45045         this.fileEl().on('change', this.uploadImage, this);
45046         
45047         this.clear();
45048         
45049         this.resize();
45050     },
45051     
45052     resize: function(){
45053         
45054         var canvas = this.canvasEl().dom;
45055         var ctx = this.canvasElCtx();
45056         var img_data = false;
45057         
45058         if(canvas.width > 0) {
45059             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45060         }
45061         // setting canvas width will clean img data
45062         canvas.width = 0;
45063         
45064         var style = window.getComputedStyle ? 
45065             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45066             
45067         var padding_left = parseInt(style.paddingLeft) || 0;
45068         var padding_right = parseInt(style.paddingRight) || 0;
45069         
45070         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45071         
45072         if(img_data) {
45073             ctx.putImageData(img_data, 0, 0);
45074         }
45075     },
45076     
45077     _handleMouseDown: function(e)
45078     {
45079         if (e.browserEvent.which === 1) {
45080             this.mouse_btn_down = true;
45081             this.strokeBegin(e);
45082         }
45083     },
45084     
45085     _handleMouseMove: function (e)
45086     {
45087         if (this.mouse_btn_down) {
45088             this.strokeMoveUpdate(e);
45089         }
45090     },
45091     
45092     _handleMouseUp: function (e)
45093     {
45094         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45095             this.mouse_btn_down = false;
45096             this.strokeEnd(e);
45097         }
45098     },
45099     
45100     _handleTouchStart: function (e) {
45101         
45102         e.preventDefault();
45103         if (e.browserEvent.targetTouches.length === 1) {
45104             // var touch = e.browserEvent.changedTouches[0];
45105             // this.strokeBegin(touch);
45106             
45107              this.strokeBegin(e); // assume e catching the correct xy...
45108         }
45109     },
45110     
45111     _handleTouchMove: function (e) {
45112         e.preventDefault();
45113         // var touch = event.targetTouches[0];
45114         // _this._strokeMoveUpdate(touch);
45115         this.strokeMoveUpdate(e);
45116     },
45117     
45118     _handleTouchEnd: function (e) {
45119         var wasCanvasTouched = e.target === this.canvasEl().dom;
45120         if (wasCanvasTouched) {
45121             e.preventDefault();
45122             // var touch = event.changedTouches[0];
45123             // _this._strokeEnd(touch);
45124             this.strokeEnd(e);
45125         }
45126     },
45127     
45128     reset: function () {
45129         this._lastPoints = [];
45130         this._lastVelocity = 0;
45131         this._lastWidth = (this.min_width + this.max_width) / 2;
45132         this.canvasElCtx().fillStyle = this.dot_color;
45133     },
45134     
45135     strokeMoveUpdate: function(e)
45136     {
45137         this.strokeUpdate(e);
45138         
45139         if (this.throttle) {
45140             this.throttleStroke(this.strokeUpdate, this.throttle);
45141         }
45142         else {
45143             this.strokeUpdate(e);
45144         }
45145     },
45146     
45147     strokeBegin: function(e)
45148     {
45149         var newPointGroup = {
45150             color: this.dot_color,
45151             points: []
45152         };
45153         
45154         if (typeof this.onBegin === 'function') {
45155             this.onBegin(e);
45156         }
45157         
45158         this.curve_data.push(newPointGroup);
45159         this.reset();
45160         this.strokeUpdate(e);
45161     },
45162     
45163     strokeUpdate: function(e)
45164     {
45165         var rect = this.canvasEl().dom.getBoundingClientRect();
45166         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45167         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45168         var lastPoints = lastPointGroup.points;
45169         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45170         var isLastPointTooClose = lastPoint
45171             ? point.distanceTo(lastPoint) <= this.min_distance
45172             : false;
45173         var color = lastPointGroup.color;
45174         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45175             var curve = this.addPoint(point);
45176             if (!lastPoint) {
45177                 this.drawDot({color: color, point: point});
45178             }
45179             else if (curve) {
45180                 this.drawCurve({color: color, curve: curve});
45181             }
45182             lastPoints.push({
45183                 time: point.time,
45184                 x: point.x,
45185                 y: point.y
45186             });
45187         }
45188     },
45189     
45190     strokeEnd: function(e)
45191     {
45192         this.strokeUpdate(e);
45193         if (typeof this.onEnd === 'function') {
45194             this.onEnd(e);
45195         }
45196     },
45197     
45198     addPoint:  function (point) {
45199         var _lastPoints = this._lastPoints;
45200         _lastPoints.push(point);
45201         if (_lastPoints.length > 2) {
45202             if (_lastPoints.length === 3) {
45203                 _lastPoints.unshift(_lastPoints[0]);
45204             }
45205             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45206             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45207             _lastPoints.shift();
45208             return curve;
45209         }
45210         return null;
45211     },
45212     
45213     calculateCurveWidths: function (startPoint, endPoint) {
45214         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45215             (1 - this.velocity_filter_weight) * this._lastVelocity;
45216
45217         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45218         var widths = {
45219             end: newWidth,
45220             start: this._lastWidth
45221         };
45222         
45223         this._lastVelocity = velocity;
45224         this._lastWidth = newWidth;
45225         return widths;
45226     },
45227     
45228     drawDot: function (_a) {
45229         var color = _a.color, point = _a.point;
45230         var ctx = this.canvasElCtx();
45231         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45232         ctx.beginPath();
45233         this.drawCurveSegment(point.x, point.y, width);
45234         ctx.closePath();
45235         ctx.fillStyle = color;
45236         ctx.fill();
45237     },
45238     
45239     drawCurve: function (_a) {
45240         var color = _a.color, curve = _a.curve;
45241         var ctx = this.canvasElCtx();
45242         var widthDelta = curve.endWidth - curve.startWidth;
45243         var drawSteps = Math.floor(curve.length()) * 2;
45244         ctx.beginPath();
45245         ctx.fillStyle = color;
45246         for (var i = 0; i < drawSteps; i += 1) {
45247         var t = i / drawSteps;
45248         var tt = t * t;
45249         var ttt = tt * t;
45250         var u = 1 - t;
45251         var uu = u * u;
45252         var uuu = uu * u;
45253         var x = uuu * curve.startPoint.x;
45254         x += 3 * uu * t * curve.control1.x;
45255         x += 3 * u * tt * curve.control2.x;
45256         x += ttt * curve.endPoint.x;
45257         var y = uuu * curve.startPoint.y;
45258         y += 3 * uu * t * curve.control1.y;
45259         y += 3 * u * tt * curve.control2.y;
45260         y += ttt * curve.endPoint.y;
45261         var width = curve.startWidth + ttt * widthDelta;
45262         this.drawCurveSegment(x, y, width);
45263         }
45264         ctx.closePath();
45265         ctx.fill();
45266     },
45267     
45268     drawCurveSegment: function (x, y, width) {
45269         var ctx = this.canvasElCtx();
45270         ctx.moveTo(x, y);
45271         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45272         this.is_empty = false;
45273     },
45274     
45275     clear: function()
45276     {
45277         var ctx = this.canvasElCtx();
45278         var canvas = this.canvasEl().dom;
45279         ctx.fillStyle = this.bg_color;
45280         ctx.clearRect(0, 0, canvas.width, canvas.height);
45281         ctx.fillRect(0, 0, canvas.width, canvas.height);
45282         this.curve_data = [];
45283         this.reset();
45284         this.is_empty = true;
45285     },
45286     
45287     fileEl: function()
45288     {
45289         return  this.el.select('input',true).first();
45290     },
45291     
45292     canvasEl: function()
45293     {
45294         return this.el.select('canvas',true).first();
45295     },
45296     
45297     canvasElCtx: function()
45298     {
45299         return this.el.select('canvas',true).first().dom.getContext('2d');
45300     },
45301     
45302     getImage: function(type)
45303     {
45304         if(this.is_empty) {
45305             return false;
45306         }
45307         
45308         // encryption ?
45309         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45310     },
45311     
45312     drawFromImage: function(img_src)
45313     {
45314         var img = new Image();
45315         
45316         img.onload = function(){
45317             this.canvasElCtx().drawImage(img, 0, 0);
45318         }.bind(this);
45319         
45320         img.src = img_src;
45321         
45322         this.is_empty = false;
45323     },
45324     
45325     selectImage: function()
45326     {
45327         this.fileEl().dom.click();
45328     },
45329     
45330     uploadImage: function(e)
45331     {
45332         var reader = new FileReader();
45333         
45334         reader.onload = function(e){
45335             var img = new Image();
45336             img.onload = function(){
45337                 this.reset();
45338                 this.canvasElCtx().drawImage(img, 0, 0);
45339             }.bind(this);
45340             img.src = e.target.result;
45341         }.bind(this);
45342         
45343         reader.readAsDataURL(e.target.files[0]);
45344     },
45345     
45346     // Bezier Point Constructor
45347     Point: (function () {
45348         function Point(x, y, time) {
45349             this.x = x;
45350             this.y = y;
45351             this.time = time || Date.now();
45352         }
45353         Point.prototype.distanceTo = function (start) {
45354             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45355         };
45356         Point.prototype.equals = function (other) {
45357             return this.x === other.x && this.y === other.y && this.time === other.time;
45358         };
45359         Point.prototype.velocityFrom = function (start) {
45360             return this.time !== start.time
45361             ? this.distanceTo(start) / (this.time - start.time)
45362             : 0;
45363         };
45364         return Point;
45365     }()),
45366     
45367     
45368     // Bezier Constructor
45369     Bezier: (function () {
45370         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45371             this.startPoint = startPoint;
45372             this.control2 = control2;
45373             this.control1 = control1;
45374             this.endPoint = endPoint;
45375             this.startWidth = startWidth;
45376             this.endWidth = endWidth;
45377         }
45378         Bezier.fromPoints = function (points, widths, scope) {
45379             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45380             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45381             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45382         };
45383         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45384             var dx1 = s1.x - s2.x;
45385             var dy1 = s1.y - s2.y;
45386             var dx2 = s2.x - s3.x;
45387             var dy2 = s2.y - s3.y;
45388             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45389             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45390             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45391             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45392             var dxm = m1.x - m2.x;
45393             var dym = m1.y - m2.y;
45394             var k = l2 / (l1 + l2);
45395             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45396             var tx = s2.x - cm.x;
45397             var ty = s2.y - cm.y;
45398             return {
45399                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45400                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45401             };
45402         };
45403         Bezier.prototype.length = function () {
45404             var steps = 10;
45405             var length = 0;
45406             var px;
45407             var py;
45408             for (var i = 0; i <= steps; i += 1) {
45409                 var t = i / steps;
45410                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45411                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45412                 if (i > 0) {
45413                     var xdiff = cx - px;
45414                     var ydiff = cy - py;
45415                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45416                 }
45417                 px = cx;
45418                 py = cy;
45419             }
45420             return length;
45421         };
45422         Bezier.prototype.point = function (t, start, c1, c2, end) {
45423             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45424             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45425             + (3.0 * c2 * (1.0 - t) * t * t)
45426             + (end * t * t * t);
45427         };
45428         return Bezier;
45429     }()),
45430     
45431     throttleStroke: function(fn, wait) {
45432       if (wait === void 0) { wait = 250; }
45433       var previous = 0;
45434       var timeout = null;
45435       var result;
45436       var storedContext;
45437       var storedArgs;
45438       var later = function () {
45439           previous = Date.now();
45440           timeout = null;
45441           result = fn.apply(storedContext, storedArgs);
45442           if (!timeout) {
45443               storedContext = null;
45444               storedArgs = [];
45445           }
45446       };
45447       return function wrapper() {
45448           var args = [];
45449           for (var _i = 0; _i < arguments.length; _i++) {
45450               args[_i] = arguments[_i];
45451           }
45452           var now = Date.now();
45453           var remaining = wait - (now - previous);
45454           storedContext = this;
45455           storedArgs = args;
45456           if (remaining <= 0 || remaining > wait) {
45457               if (timeout) {
45458                   clearTimeout(timeout);
45459                   timeout = null;
45460               }
45461               previous = now;
45462               result = fn.apply(storedContext, storedArgs);
45463               if (!timeout) {
45464                   storedContext = null;
45465                   storedArgs = [];
45466               }
45467           }
45468           else if (!timeout) {
45469               timeout = window.setTimeout(later, remaining);
45470           }
45471           return result;
45472       };
45473   }
45474   
45475 });
45476
45477  
45478
45479